@open-mercato/core 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4393.1.de282b5dfd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/channel_ingest_dead_letter/index.js +25 -0
- package/dist/generated/entities/channel_ingest_dead_letter/index.js.map +7 -0
- package/dist/generated/entities/channel_thread_mapping/index.js +25 -0
- package/dist/generated/entities/channel_thread_mapping/index.js.map +7 -0
- package/dist/generated/entities/channel_thread_token/index.js +17 -0
- package/dist/generated/entities/channel_thread_token/index.js.map +7 -0
- package/dist/generated/entities/communication_channel/index.js +43 -0
- package/dist/generated/entities/communication_channel/index.js.map +7 -0
- package/dist/generated/entities/customer_interaction/index.js +4 -0
- package/dist/generated/entities/customer_interaction/index.js.map +2 -2
- package/dist/generated/entities/external_conversation/index.js +25 -0
- package/dist/generated/entities/external_conversation/index.js.map +7 -0
- package/dist/generated/entities/external_message/index.js +25 -0
- package/dist/generated/entities/external_message/index.js.map +7 -0
- package/dist/generated/entities/integration_credentials/index.js +3 -1
- package/dist/generated/entities/integration_credentials/index.js.map +2 -2
- package/dist/generated/entities/message/index.js +2 -0
- package/dist/generated/entities/message/index.js.map +2 -2
- package/dist/generated/entities/message_channel_link/index.js +33 -0
- package/dist/generated/entities/message_channel_link/index.js.map +7 -0
- package/dist/generated/entities/message_reaction/index.js +25 -0
- package/dist/generated/entities/message_reaction/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +11 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +117 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/authFixtures.js +2 -1
- package/dist/helpers/integration/authFixtures.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +58 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +7 -0
- package/dist/modules/communication_channels/acl.js +47 -0
- package/dist/modules/communication_channels/acl.js.map +7 -0
- package/dist/modules/communication_channels/api/delete/channels/[id]/route.js +133 -0
- package/dist/modules/communication_channels/api/delete/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js +113 -0
- package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +138 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js +93 -0
- package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/channels/route.js +96 -0
- package/dist/modules/communication_channels/api/get/channels/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/me/channels/route.js +82 -0
- package/dist/modules/communication_channels/api/get/me/channels/route.js.map +7 -0
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +274 -0
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +168 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js +143 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +127 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js +99 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +197 -0
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +124 -0
- package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js +120 -0
- package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +157 -0
- package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/send-as-user/route.js +115 -0
- package/dist/modules/communication_channels/api/post/send-as-user/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/test-seed/route.js +217 -0
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +175 -0
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +7 -0
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +123 -0
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +7 -0
- package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js +117 -0
- package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js +180 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js +36 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +107 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js +38 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +727 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +38 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +7 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js +154 -0
- package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/delete-channel.js +137 -0
- package/dist/modules/communication_channels/commands/delete-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +400 -0
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +7 -0
- package/dist/modules/communication_channels/commands/disconnect-channel.js +163 -0
- package/dist/modules/communication_channels/commands/disconnect-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +413 -0
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +7 -0
- package/dist/modules/communication_channels/commands/interceptors.js +68 -0
- package/dist/modules/communication_channels/commands/interceptors.js.map +7 -0
- package/dist/modules/communication_channels/commands/process-inbound-reaction.js +198 -0
- package/dist/modules/communication_channels/commands/process-inbound-reaction.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-register.js +146 -0
- package/dist/modules/communication_channels/commands/push-register.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-renew.js +23 -0
- package/dist/modules/communication_channels/commands/push-renew.js.map +7 -0
- package/dist/modules/communication_channels/commands/push-unregister.js +108 -0
- package/dist/modules/communication_channels/commands/push-unregister.js.map +7 -0
- package/dist/modules/communication_channels/commands/queue-import-history.js +113 -0
- package/dist/modules/communication_channels/commands/queue-import-history.js.map +7 -0
- package/dist/modules/communication_channels/commands/reassign-conversation.js +193 -0
- package/dist/modules/communication_channels/commands/reassign-conversation.js.map +7 -0
- package/dist/modules/communication_channels/commands/set-primary-channel.js +114 -0
- package/dist/modules/communication_channels/commands/set-primary-channel.js.map +7 -0
- package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js +260 -0
- package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js.map +7 -0
- package/dist/modules/communication_channels/data/enrichers.js +286 -0
- package/dist/modules/communication_channels/data/enrichers.js.map +7 -0
- package/dist/modules/communication_channels/data/entities.js +447 -0
- package/dist/modules/communication_channels/data/entities.js.map +7 -0
- package/dist/modules/communication_channels/data/extensions.js +67 -0
- package/dist/modules/communication_channels/data/extensions.js.map +7 -0
- package/dist/modules/communication_channels/data/validators.js +123 -0
- package/dist/modules/communication_channels/data/validators.js.map +7 -0
- package/dist/modules/communication_channels/di.js +35 -0
- package/dist/modules/communication_channels/di.js.map +7 -0
- package/dist/modules/communication_channels/encryption.js +12 -0
- package/dist/modules/communication_channels/encryption.js.map +7 -0
- package/dist/modules/communication_channels/events.js +124 -0
- package/dist/modules/communication_channels/events.js.map +7 -0
- package/dist/modules/communication_channels/index.js +20 -0
- package/dist/modules/communication_channels/index.js.map +7 -0
- package/dist/modules/communication_channels/lib/access-control.js +43 -0
- package/dist/modules/communication_channels/lib/access-control.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter-compat.js +36 -0
- package/dist/modules/communication_channels/lib/adapter-compat.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter-registry-singleton.js +22 -0
- package/dist/modules/communication_channels/lib/adapter-registry-singleton.js.map +7 -0
- package/dist/modules/communication_channels/lib/adapter.js +1 -0
- package/dist/modules/communication_channels/lib/adapter.js.map +7 -0
- package/dist/modules/communication_channels/lib/connect-channel.js +95 -0
- package/dist/modules/communication_channels/lib/connect-channel.js.map +7 -0
- package/dist/modules/communication_channels/lib/contact-resolver.js +79 -0
- package/dist/modules/communication_channels/lib/contact-resolver.js.map +7 -0
- package/dist/modules/communication_channels/lib/credential-refresh.js +97 -0
- package/dist/modules/communication_channels/lib/credential-refresh.js.map +7 -0
- package/dist/modules/communication_channels/lib/dead-letter.js +62 -0
- package/dist/modules/communication_channels/lib/dead-letter.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-capabilities.js +47 -0
- package/dist/modules/communication_channels/lib/email-capabilities.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-contact.js +14 -0
- package/dist/modules/communication_channels/lib/email-contact.js.map +7 -0
- package/dist/modules/communication_channels/lib/email-mime.js +259 -0
- package/dist/modules/communication_channels/lib/email-mime.js.map +7 -0
- package/dist/modules/communication_channels/lib/error-classification.js +101 -0
- package/dist/modules/communication_channels/lib/error-classification.js.map +7 -0
- package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js +185 -0
- package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js.map +7 -0
- package/dist/modules/communication_channels/lib/mutation-guards.js +114 -0
- package/dist/modules/communication_channels/lib/mutation-guards.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-client-config.js +32 -0
- package/dist/modules/communication_channels/lib/oauth-client-config.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-state.js +128 -0
- package/dist/modules/communication_channels/lib/oauth-state.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-token.js +45 -0
- package/dist/modules/communication_channels/lib/oauth-token.js.map +7 -0
- package/dist/modules/communication_channels/lib/pg-errors.js +11 -0
- package/dist/modules/communication_channels/lib/pg-errors.js.map +7 -0
- package/dist/modules/communication_channels/lib/provider-health.js +24 -0
- package/dist/modules/communication_channels/lib/provider-health.js.map +7 -0
- package/dist/modules/communication_channels/lib/push-state.js +19 -0
- package/dist/modules/communication_channels/lib/push-state.js.map +7 -0
- package/dist/modules/communication_channels/lib/queue.js +54 -0
- package/dist/modules/communication_channels/lib/queue.js.map +7 -0
- package/dist/modules/communication_channels/lib/reaction-processor-types.js +5 -0
- package/dist/modules/communication_channels/lib/reaction-processor-types.js.map +7 -0
- package/dist/modules/communication_channels/lib/reaction-semantics.js +11 -0
- package/dist/modules/communication_channels/lib/reaction-semantics.js.map +7 -0
- package/dist/modules/communication_channels/lib/registry.js +67 -0
- package/dist/modules/communication_channels/lib/registry.js.map +7 -0
- package/dist/modules/communication_channels/lib/route-mutation-guard.js +43 -0
- package/dist/modules/communication_channels/lib/route-mutation-guard.js.map +7 -0
- package/dist/modules/communication_channels/lib/sanitize-channel-html.js +96 -0
- package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +7 -0
- package/dist/modules/communication_channels/lib/send-as-user.js +194 -0
- package/dist/modules/communication_channels/lib/send-as-user.js.map +7 -0
- package/dist/modules/communication_channels/lib/system-user.js +22 -0
- package/dist/modules/communication_channels/lib/system-user.js.map +7 -0
- package/dist/modules/communication_channels/lib/test-seed.js +68 -0
- package/dist/modules/communication_channels/lib/test-seed.js.map +7 -0
- package/dist/modules/communication_channels/lib/thread-matcher.js +263 -0
- package/dist/modules/communication_channels/lib/thread-matcher.js.map +7 -0
- package/dist/modules/communication_channels/lib/thread-token.js +219 -0
- package/dist/modules/communication_channels/lib/thread-token.js.map +7 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js +61 -0
- package/dist/modules/communication_channels/lib/use-connect-channel.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js +50 -0
- package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js +19 -0
- package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js +13 -0
- package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js +17 -0
- package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/notifications.client.js +51 -0
- package/dist/modules/communication_channels/notifications.client.js.map +7 -0
- package/dist/modules/communication_channels/notifications.handlers.js +53 -0
- package/dist/modules/communication_channels/notifications.handlers.js.map +7 -0
- package/dist/modules/communication_channels/notifications.js +56 -0
- package/dist/modules/communication_channels/notifications.js.map +7 -0
- package/dist/modules/communication_channels/setup.js +105 -0
- package/dist/modules/communication_channels/setup.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js +71 -0
- package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/outbound-bridge.js +103 -0
- package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +7 -0
- package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js +51 -0
- package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js.map +7 -0
- package/dist/modules/communication_channels/widgets/components.js +7 -0
- package/dist/modules/communication_channels/widgets/components.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js +18 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js +30 -0
- package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js +185 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +44 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js +23 -0
- package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js +141 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js +17 -0
- package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js.map +7 -0
- package/dist/modules/communication_channels/widgets/injection-table.js +38 -0
- package/dist/modules/communication_channels/widgets/injection-table.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js +25 -0
- package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js +19 -0
- package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js.map +7 -0
- package/dist/modules/communication_channels/widgets/notifications/index.js +7 -0
- package/dist/modules/communication_channels/widgets/notifications/index.js.map +7 -0
- package/dist/modules/communication_channels/workers/channel-import-history.js +185 -0
- package/dist/modules/communication_channels/workers/channel-import-history.js.map +7 -0
- package/dist/modules/communication_channels/workers/gmail-history-sync.js +154 -0
- package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +7 -0
- package/dist/modules/communication_channels/workers/gmail-renew-watch.js +95 -0
- package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +7 -0
- package/dist/modules/communication_channels/workers/inbound-processor.js +56 -0
- package/dist/modules/communication_channels/workers/inbound-processor.js.map +7 -0
- package/dist/modules/communication_channels/workers/outbound-delivery.js +85 -0
- package/dist/modules/communication_channels/workers/outbound-delivery.js.map +7 -0
- package/dist/modules/communication_channels/workers/poll-channel.js +240 -0
- package/dist/modules/communication_channels/workers/poll-channel.js.map +7 -0
- package/dist/modules/communication_channels/workers/poll-tick.js +132 -0
- package/dist/modules/communication_channels/workers/poll-tick.js.map +7 -0
- package/dist/modules/communication_channels/workers/reaction-processor.js +192 -0
- package/dist/modules/communication_channels/workers/reaction-processor.js.map +7 -0
- package/dist/modules/customers/acl.js +18 -0
- package/dist/modules/customers/acl.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +9 -0
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +18 -7
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/[id]/visibility/route.js +151 -0
- package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +7 -0
- package/dist/modules/customers/api/interactions/counts/route.js +6 -0
- package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +26 -7
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/email-threads/route.js +82 -0
- package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +7 -0
- package/dist/modules/customers/api/people/[id]/emails/route.js +157 -0
- package/dist/modules/customers/api/people/[id]/emails/route.js.map +7 -0
- package/dist/modules/customers/api/people/[id]/route.js +12 -4
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +10 -0
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +46 -5
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +16 -0
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityCard.js +32 -0
- package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
- package/dist/modules/customers/components/detail/ComposeEmailDialog.js +242 -0
- package/dist/modules/customers/components/detail/ComposeEmailDialog.js.map +7 -0
- package/dist/modules/customers/components/detail/DealForm.js +2 -1
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +10 -0
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/EmailCardActions.js +179 -0
- package/dist/modules/customers/components/detail/EmailCardActions.js.map +7 -0
- package/dist/modules/customers/components/detail/EmailReplyForwardActions.js +52 -0
- package/dist/modules/customers/components/detail/EmailReplyForwardActions.js.map +7 -0
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +7 -1
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +366 -0
- package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +7 -0
- package/dist/modules/customers/data/enrichers.js +133 -2
- package/dist/modules/customers/data/enrichers.js.map +2 -2
- package/dist/modules/customers/data/entities.js +18 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/extensions.js +16 -0
- package/dist/modules/customers/data/extensions.js.map +7 -0
- package/dist/modules/customers/encryption.js +11 -0
- package/dist/modules/customers/encryption.js.map +2 -2
- package/dist/modules/customers/events.js +4 -1
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/findPeopleByAddresses.js +64 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js.map +7 -0
- package/dist/modules/customers/lib/kysely.js.map +2 -2
- package/dist/modules/customers/lib/link-channel-message-handler.js +303 -0
- package/dist/modules/customers/lib/link-channel-message-handler.js.map +7 -0
- package/dist/modules/customers/lib/personEmailThreads.js +205 -0
- package/dist/modules/customers/lib/personEmailThreads.js.map +7 -0
- package/dist/modules/customers/lib/visibilityFilter.js +51 -0
- package/dist/modules/customers/lib/visibilityFilter.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260527012240_customers.js +20 -0
- package/dist/modules/customers/migrations/Migration20260527012240_customers.js.map +7 -0
- package/dist/modules/customers/setup.js +2 -1
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/customers/subscribers/link-channel-message-received.js +12 -0
- package/dist/modules/customers/subscribers/link-channel-message-received.js.map +7 -0
- package/dist/modules/customers/subscribers/link-channel-message-sent.js +12 -0
- package/dist/modules/customers/subscribers/link-channel-message-sent.js.map +7 -0
- package/dist/modules/integrations/data/entities.js +8 -1
- package/dist/modules/integrations/data/entities.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-service.js +29 -14
- package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
- package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js +15 -0
- package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js.map +7 -0
- package/dist/modules/messages/commands/messages.js +70 -8
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -13
- package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
- package/dist/modules/messages/components/MessageDetailPageClient.js +39 -2
- package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -0
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/data/entities.js +8 -1
- package/dist/modules/messages/data/entities.js.map +2 -2
- package/dist/modules/messages/migrations/Migration20260531130000.js +15 -0
- package/dist/modules/messages/migrations/Migration20260531130000.js.map +7 -0
- package/dist/modules/messages/widgets/injection-table.js +7 -0
- package/dist/modules/messages/widgets/injection-table.js.map +7 -0
- package/generated/entities/channel_ingest_dead_letter/index.ts +11 -0
- package/generated/entities/channel_thread_mapping/index.ts +11 -0
- package/generated/entities/channel_thread_token/index.ts +7 -0
- package/generated/entities/communication_channel/index.ts +20 -0
- package/generated/entities/customer_interaction/index.ts +2 -0
- package/generated/entities/external_conversation/index.ts +11 -0
- package/generated/entities/external_message/index.ts +11 -0
- package/generated/entities/integration_credentials/index.ts +1 -0
- package/generated/entities/message/index.ts +1 -0
- package/generated/entities/message_channel_link/index.ts +15 -0
- package/generated/entities/message_reaction/index.ts +11 -0
- package/generated/entities.ids.generated.ts +11 -0
- package/generated/entity-fields-registry.ts +117 -0
- package/package.json +9 -7
- package/src/helpers/integration/authFixtures.ts +4 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +124 -0
- package/src/modules/communication_channels/acl.ts +43 -0
- package/src/modules/communication_channels/api/delete/channels/[id]/route.ts +163 -0
- package/src/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.ts +143 -0
- package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +173 -0
- package/src/modules/communication_channels/api/get/channels/[id]/route.ts +111 -0
- package/src/modules/communication_channels/api/get/channels/route.ts +109 -0
- package/src/modules/communication_channels/api/get/me/channels/route.ts +100 -0
- package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +355 -0
- package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +206 -0
- package/src/modules/communication_channels/api/post/channels/[id]/poll-now/route.ts +174 -0
- package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +158 -0
- package/src/modules/communication_channels/api/post/channels/[id]/set-primary/route.ts +114 -0
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +241 -0
- package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +134 -0
- package/src/modules/communication_channels/api/post/messages/[messageId]/reactions/route.ts +143 -0
- package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +192 -0
- package/src/modules/communication_channels/api/post/send-as-user/route.ts +125 -0
- package/src/modules/communication_channels/api/post/test-seed/route.ts +267 -0
- package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +227 -0
- package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +161 -0
- package/src/modules/communication_channels/api/put/threads/[threadId]/assign/route.ts +132 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.ts +34 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.tsx +250 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.meta.ts +36 -0
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +137 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +36 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +907 -0
- package/src/modules/communication_channels/commands/connect-credential-channel.ts +243 -0
- package/src/modules/communication_channels/commands/delete-channel.ts +193 -0
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +579 -0
- package/src/modules/communication_channels/commands/disconnect-channel.ts +241 -0
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +602 -0
- package/src/modules/communication_channels/commands/interceptors.ts +104 -0
- package/src/modules/communication_channels/commands/process-inbound-reaction.ts +265 -0
- package/src/modules/communication_channels/commands/push-register.ts +203 -0
- package/src/modules/communication_channels/commands/push-renew.ts +49 -0
- package/src/modules/communication_channels/commands/push-unregister.ts +168 -0
- package/src/modules/communication_channels/commands/queue-import-history.ts +180 -0
- package/src/modules/communication_channels/commands/reassign-conversation.ts +273 -0
- package/src/modules/communication_channels/commands/set-primary-channel.ts +154 -0
- package/src/modules/communication_channels/commands/toggle-outbound-reaction.ts +347 -0
- package/src/modules/communication_channels/data/enrichers.ts +413 -0
- package/src/modules/communication_channels/data/entities.ts +546 -0
- package/src/modules/communication_channels/data/extensions.ts +76 -0
- package/src/modules/communication_channels/data/validators.ts +138 -0
- package/src/modules/communication_channels/di.ts +40 -0
- package/src/modules/communication_channels/encryption.ts +44 -0
- package/src/modules/communication_channels/events.ts +122 -0
- package/src/modules/communication_channels/i18n/de.json +138 -0
- package/src/modules/communication_channels/i18n/en.json +138 -0
- package/src/modules/communication_channels/i18n/es.json +138 -0
- package/src/modules/communication_channels/i18n/pl.json +138 -0
- package/src/modules/communication_channels/index.ts +19 -0
- package/src/modules/communication_channels/lib/access-control.ts +110 -0
- package/src/modules/communication_channels/lib/adapter-compat.ts +57 -0
- package/src/modules/communication_channels/lib/adapter-registry-singleton.ts +35 -0
- package/src/modules/communication_channels/lib/adapter.ts +605 -0
- package/src/modules/communication_channels/lib/connect-channel.ts +163 -0
- package/src/modules/communication_channels/lib/contact-resolver.ts +162 -0
- package/src/modules/communication_channels/lib/credential-refresh.ts +197 -0
- package/src/modules/communication_channels/lib/dead-letter.ts +87 -0
- package/src/modules/communication_channels/lib/email-capabilities.ts +60 -0
- package/src/modules/communication_channels/lib/email-contact.ts +17 -0
- package/src/modules/communication_channels/lib/email-mime.ts +425 -0
- package/src/modules/communication_channels/lib/error-classification.ts +144 -0
- package/src/modules/communication_channels/lib/gmail-pubsub-jwt.ts +278 -0
- package/src/modules/communication_channels/lib/mutation-guards.ts +215 -0
- package/src/modules/communication_channels/lib/oauth-client-config.ts +79 -0
- package/src/modules/communication_channels/lib/oauth-state.ts +228 -0
- package/src/modules/communication_channels/lib/oauth-token.ts +81 -0
- package/src/modules/communication_channels/lib/pg-errors.ts +12 -0
- package/src/modules/communication_channels/lib/provider-health.ts +47 -0
- package/src/modules/communication_channels/lib/push-state.ts +38 -0
- package/src/modules/communication_channels/lib/queue.ts +66 -0
- package/src/modules/communication_channels/lib/reaction-processor-types.ts +51 -0
- package/src/modules/communication_channels/lib/reaction-semantics.ts +48 -0
- package/src/modules/communication_channels/lib/registry.ts +99 -0
- package/src/modules/communication_channels/lib/route-mutation-guard.ts +68 -0
- package/src/modules/communication_channels/lib/sanitize-channel-html.ts +129 -0
- package/src/modules/communication_channels/lib/send-as-user.ts +284 -0
- package/src/modules/communication_channels/lib/system-user.ts +74 -0
- package/src/modules/communication_channels/lib/test-seed.ts +140 -0
- package/src/modules/communication_channels/lib/thread-matcher.ts +430 -0
- package/src/modules/communication_channels/lib/thread-token.ts +355 -0
- package/src/modules/communication_channels/lib/use-connect-channel.ts +73 -0
- package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +2142 -0
- package/src/modules/communication_channels/migrations/Migration20260526134719_communication_channels.ts +55 -0
- package/src/modules/communication_channels/migrations/Migration20260527195446_communication_channels.ts +20 -0
- package/src/modules/communication_channels/migrations/Migration20260529231848_communication_channels.ts +13 -0
- package/src/modules/communication_channels/migrations/Migration20260531120000_communication_channels.ts +24 -0
- package/src/modules/communication_channels/notifications.client.ts +50 -0
- package/src/modules/communication_channels/notifications.handlers.ts +86 -0
- package/src/modules/communication_channels/notifications.ts +52 -0
- package/src/modules/communication_channels/setup.ts +158 -0
- package/src/modules/communication_channels/subscribers/channel-requires-reauth-notification.ts +118 -0
- package/src/modules/communication_channels/subscribers/outbound-bridge.ts +175 -0
- package/src/modules/communication_channels/subscribers/user-deleted-cascade.ts +100 -0
- package/src/modules/communication_channels/widgets/components.ts +36 -0
- package/src/modules/communication_channels/widgets/injection/channel-badge/widget.client.tsx +38 -0
- package/src/modules/communication_channels/widgets/injection/channel-badge/widget.ts +51 -0
- package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.tsx +278 -0
- package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.ts +24 -0
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +63 -0
- package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +29 -0
- package/src/modules/communication_channels/widgets/injection/profile-channels-menu/widget.ts +34 -0
- package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.client.tsx +177 -0
- package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.ts +26 -0
- package/src/modules/communication_channels/widgets/injection-table.ts +47 -0
- package/src/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.tsx +48 -0
- package/src/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.tsx +45 -0
- package/src/modules/communication_channels/widgets/notifications/index.ts +2 -0
- package/src/modules/communication_channels/workers/channel-import-history.ts +252 -0
- package/src/modules/communication_channels/workers/gmail-history-sync.ts +223 -0
- package/src/modules/communication_channels/workers/gmail-renew-watch.ts +141 -0
- package/src/modules/communication_channels/workers/inbound-processor.ts +114 -0
- package/src/modules/communication_channels/workers/outbound-delivery.ts +155 -0
- package/src/modules/communication_channels/workers/poll-channel.ts +391 -0
- package/src/modules/communication_channels/workers/poll-tick.ts +210 -0
- package/src/modules/communication_channels/workers/reaction-processor.ts +264 -0
- package/src/modules/customers/acl.ts +18 -0
- package/src/modules/customers/api/activities/route.ts +13 -0
- package/src/modules/customers/api/companies/[id]/route.ts +21 -1
- package/src/modules/customers/api/interactions/[id]/visibility/route.ts +179 -0
- package/src/modules/customers/api/interactions/counts/route.ts +10 -0
- package/src/modules/customers/api/interactions/route.ts +51 -5
- package/src/modules/customers/api/people/[id]/email-threads/route.ts +92 -0
- package/src/modules/customers/api/people/[id]/emails/route.ts +184 -0
- package/src/modules/customers/api/people/[id]/route.ts +17 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +11 -1
- package/src/modules/customers/commands/deals.ts +65 -6
- package/src/modules/customers/commands/interactions.ts +30 -0
- package/src/modules/customers/components/detail/ActivityCard.tsx +48 -0
- package/src/modules/customers/components/detail/ComposeEmailDialog.tsx +329 -0
- package/src/modules/customers/components/detail/DealForm.tsx +2 -1
- package/src/modules/customers/components/detail/DealsSection.tsx +26 -0
- package/src/modules/customers/components/detail/EmailCardActions.tsx +258 -0
- package/src/modules/customers/components/detail/EmailReplyForwardActions.tsx +53 -0
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +8 -1
- package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +448 -0
- package/src/modules/customers/data/enrichers.ts +252 -1
- package/src/modules/customers/data/entities.ts +46 -1
- package/src/modules/customers/data/extensions.ts +26 -0
- package/src/modules/customers/encryption.ts +11 -0
- package/src/modules/customers/events.ts +4 -0
- package/src/modules/customers/i18n/de.json +41 -0
- package/src/modules/customers/i18n/en.json +41 -0
- package/src/modules/customers/i18n/es.json +41 -0
- package/src/modules/customers/i18n/pl.json +41 -0
- package/src/modules/customers/lib/findPeopleByAddresses.ts +107 -0
- package/src/modules/customers/lib/kysely.ts +16 -0
- package/src/modules/customers/lib/link-channel-message-handler.ts +571 -0
- package/src/modules/customers/lib/personEmailThreads.ts +325 -0
- package/src/modules/customers/lib/visibilityFilter.ts +152 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +61 -0
- package/src/modules/customers/migrations/Migration20260527012240_customers.ts +23 -0
- package/src/modules/customers/setup.ts +1 -0
- package/src/modules/customers/subscribers/link-channel-message-received.ts +21 -0
- package/src/modules/customers/subscribers/link-channel-message-sent.ts +21 -0
- package/src/modules/integrations/AGENTS.md +9 -0
- package/src/modules/integrations/data/entities.ts +21 -1
- package/src/modules/integrations/lib/credentials-service.ts +49 -13
- package/src/modules/integrations/migrations/.snapshot-open-mercato.json +26 -1
- package/src/modules/integrations/migrations/Migration20260526154136_integrations.ts +15 -0
- package/src/modules/messages/commands/messages.ts +101 -8
- package/src/modules/messages/components/ComposeMessagePageClient.tsx +17 -0
- package/src/modules/messages/components/MessageDetailPageClient.tsx +43 -0
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -0
- package/src/modules/messages/data/entities.ts +11 -0
- package/src/modules/messages/migrations/.snapshot-open-mercato.json +18 -0
- package/src/modules/messages/migrations/Migration20260531130000.ts +15 -0
- package/src/modules/messages/widgets/injection-table.ts +29 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
function allowsMultipleReactionsPerUser(capabilities) {
|
|
2
|
+
return capabilities?.multiReactionPerUser === true;
|
|
3
|
+
}
|
|
4
|
+
function resolveInboundAddMutation(capabilities) {
|
|
5
|
+
return allowsMultipleReactionsPerUser(capabilities) ? "insert" : "replace";
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
allowsMultipleReactionsPerUser,
|
|
9
|
+
resolveInboundAddMutation
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=reaction-semantics.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/communication_channels/lib/reaction-semantics.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ChannelCapabilities } from './adapter'\n\n/**\n * Reaction semantics \u2014 capability-driven decisions about how the hub applies an\n * incoming or outgoing reaction.\n *\n * The platform-side `MessageReaction` table can record any number of rows per\n * (messageId, reactor); the channel-specific semantics decide whether to keep\n * existing reactions when a new one arrives:\n *\n * - **Multi-per-user** (Slack default; `multiReactionPerUser: true`):\n * keep all existing reactions from the same reactor; just insert the new one.\n *\n * - **Single-per-user** (WhatsApp default; `multiReactionPerUser: false`):\n * delete all existing reactions from the same reactor for the same message,\n * then insert the new one. This matches WhatsApp's \"one reaction per user\"\n * UI behaviour.\n *\n * The helper functions here are pure \u2014 they don't talk to the database. The\n * command layer that consumes them is responsible for the actual DELETE/INSERT.\n */\n\n/**\n * Returns true when the channel's capabilities allow multiple reactions from\n * the same reactor on the same message (Slack-like).\n */\nexport function allowsMultipleReactionsPerUser(\n capabilities: Pick<ChannelCapabilities, 'multiReactionPerUser'> | null | undefined,\n): boolean {\n // Default to false for safety \u2014 a missing capability declaration should not\n // accidentally enable multi-reactions. Real adapters explicitly declare both\n // booleans per SPEC-045d \u00A71.1.\n return capabilities?.multiReactionPerUser === true\n}\n\n/**\n * Computes what mutation a new inbound `added` reaction implies, given the\n * channel's capabilities. Pure \u2014 no DB calls. The caller maps the result to\n * SQL.\n *\n * @returns `'insert'` to just persist the new reaction; `'replace'` to delete\n * every reaction from the same reactor for the same message before inserting.\n */\nexport function resolveInboundAddMutation(\n capabilities: Pick<ChannelCapabilities, 'multiReactionPerUser'> | null | undefined,\n): 'insert' | 'replace' {\n return allowsMultipleReactionsPerUser(capabilities) ? 'insert' : 'replace'\n}\n"],
|
|
5
|
+
"mappings": "AA0BO,SAAS,+BACd,cACS;AAIT,SAAO,cAAc,yBAAyB;AAChD;AAUO,SAAS,0BACd,cACsB;AACtB,SAAO,+BAA+B,YAAY,IAAI,WAAW;AACnE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { validateAdapterCapabilities } from "./adapter-compat.js";
|
|
2
|
+
const CHANNEL_ADAPTER_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("@open-mercato/communication-channels/adapter-registry");
|
|
3
|
+
function getRegistryMap() {
|
|
4
|
+
const scope = globalThis;
|
|
5
|
+
if (!scope[CHANNEL_ADAPTER_REGISTRY_KEY]) {
|
|
6
|
+
scope[CHANNEL_ADAPTER_REGISTRY_KEY] = /* @__PURE__ */ new Map();
|
|
7
|
+
}
|
|
8
|
+
return scope[CHANNEL_ADAPTER_REGISTRY_KEY];
|
|
9
|
+
}
|
|
10
|
+
function registerChannelAdapter(adapter) {
|
|
11
|
+
validateAdapterCapabilities(adapter);
|
|
12
|
+
const map = getRegistryMap();
|
|
13
|
+
if (map.has(adapter.providerKey)) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`ChannelAdapter '${adapter.providerKey}' is already registered. Each provider package must declare a unique providerKey.`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
map.set(adapter.providerKey, adapter);
|
|
19
|
+
return () => {
|
|
20
|
+
map.delete(adapter.providerKey);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function getChannelAdapter(providerKey) {
|
|
24
|
+
return getRegistryMap().get(providerKey);
|
|
25
|
+
}
|
|
26
|
+
function listChannelAdapters() {
|
|
27
|
+
return Array.from(getRegistryMap().values());
|
|
28
|
+
}
|
|
29
|
+
function listChannelAdapterProviderKeys() {
|
|
30
|
+
return Array.from(getRegistryMap().keys());
|
|
31
|
+
}
|
|
32
|
+
function hasChannelAdapter(providerKey) {
|
|
33
|
+
return getRegistryMap().has(providerKey);
|
|
34
|
+
}
|
|
35
|
+
function clearChannelAdapters() {
|
|
36
|
+
getRegistryMap().clear();
|
|
37
|
+
}
|
|
38
|
+
class ChannelAdapterRegistry {
|
|
39
|
+
register(adapter) {
|
|
40
|
+
registerChannelAdapter(adapter);
|
|
41
|
+
}
|
|
42
|
+
get(providerKey) {
|
|
43
|
+
return getChannelAdapter(providerKey);
|
|
44
|
+
}
|
|
45
|
+
list() {
|
|
46
|
+
return listChannelAdapters();
|
|
47
|
+
}
|
|
48
|
+
providerKeys() {
|
|
49
|
+
return listChannelAdapterProviderKeys();
|
|
50
|
+
}
|
|
51
|
+
has(providerKey) {
|
|
52
|
+
return hasChannelAdapter(providerKey);
|
|
53
|
+
}
|
|
54
|
+
clear() {
|
|
55
|
+
clearChannelAdapters();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
ChannelAdapterRegistry,
|
|
60
|
+
clearChannelAdapters,
|
|
61
|
+
getChannelAdapter,
|
|
62
|
+
hasChannelAdapter,
|
|
63
|
+
listChannelAdapterProviderKeys,
|
|
64
|
+
listChannelAdapters,
|
|
65
|
+
registerChannelAdapter
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/communication_channels/lib/registry.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ChannelAdapter } from './adapter'\nimport { validateAdapterCapabilities } from './adapter-compat'\n\n/**\n * Channel adapter registry \u2014 process-level singleton backed by `globalThis`.\n *\n * Mirrors the `shipping_carriers/lib/adapter-registry.ts` pattern so that\n * unauthenticated webhook routes (which have not yet built a tenant-scoped DI\n * container) can resolve adapters by `providerKey`. DI consumers continue to\n * resolve the same registry via the `channelAdapterRegistry` binding declared\n * in `di.ts` \u2014 that binding is a thin proxy over these functions.\n *\n * The registry validates each adapter at registration time (see\n * `validateAdapterCapabilities`) and refuses duplicate `providerKey` registrations.\n */\n\nconst CHANNEL_ADAPTER_REGISTRY_KEY = Symbol.for('@open-mercato/communication-channels/adapter-registry')\n\ntype GlobalWithChannelRegistry = typeof globalThis & {\n [CHANNEL_ADAPTER_REGISTRY_KEY]?: Map<string, ChannelAdapter>\n}\n\nfunction getRegistryMap(): Map<string, ChannelAdapter> {\n const scope = globalThis as GlobalWithChannelRegistry\n if (!scope[CHANNEL_ADAPTER_REGISTRY_KEY]) {\n scope[CHANNEL_ADAPTER_REGISTRY_KEY] = new Map<string, ChannelAdapter>()\n }\n return scope[CHANNEL_ADAPTER_REGISTRY_KEY]!\n}\n\nexport function registerChannelAdapter(adapter: ChannelAdapter): () => void {\n validateAdapterCapabilities(adapter)\n const map = getRegistryMap()\n if (map.has(adapter.providerKey)) {\n throw new Error(\n `ChannelAdapter '${adapter.providerKey}' is already registered. ` +\n 'Each provider package must declare a unique providerKey.',\n )\n }\n map.set(adapter.providerKey, adapter)\n return () => {\n map.delete(adapter.providerKey)\n }\n}\n\nexport function getChannelAdapter(providerKey: string): ChannelAdapter | undefined {\n return getRegistryMap().get(providerKey)\n}\n\nexport function listChannelAdapters(): ChannelAdapter[] {\n return Array.from(getRegistryMap().values())\n}\n\nexport function listChannelAdapterProviderKeys(): string[] {\n return Array.from(getRegistryMap().keys())\n}\n\nexport function hasChannelAdapter(providerKey: string): boolean {\n return getRegistryMap().has(providerKey)\n}\n\n/**\n * Clear the registry. Primarily for tests that need a fresh registry between cases.\n * NOT for production use \u2014 at runtime, adapters are registered once at boot.\n */\nexport function clearChannelAdapters(): void {\n getRegistryMap().clear()\n}\n\n/**\n * Class wrapper kept for DI consumers and test ergonomics. All instances back\n * onto the same `globalThis` storage so DI consumers and direct module callers\n * see the same registry.\n */\nexport class ChannelAdapterRegistry {\n register(adapter: ChannelAdapter): void {\n registerChannelAdapter(adapter)\n }\n\n get(providerKey: string): ChannelAdapter | undefined {\n return getChannelAdapter(providerKey)\n }\n\n list(): ChannelAdapter[] {\n return listChannelAdapters()\n }\n\n providerKeys(): string[] {\n return listChannelAdapterProviderKeys()\n }\n\n has(providerKey: string): boolean {\n return hasChannelAdapter(providerKey)\n }\n\n clear(): void {\n clearChannelAdapters()\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,mCAAmC;AAe5C,MAAM,+BAA+B,uBAAO,IAAI,uDAAuD;AAMvG,SAAS,iBAA8C;AACrD,QAAM,QAAQ;AACd,MAAI,CAAC,MAAM,4BAA4B,GAAG;AACxC,UAAM,4BAA4B,IAAI,oBAAI,IAA4B;AAAA,EACxE;AACA,SAAO,MAAM,4BAA4B;AAC3C;AAEO,SAAS,uBAAuB,SAAqC;AAC1E,8BAA4B,OAAO;AACnC,QAAM,MAAM,eAAe;AAC3B,MAAI,IAAI,IAAI,QAAQ,WAAW,GAAG;AAChC,UAAM,IAAI;AAAA,MACR,mBAAmB,QAAQ,WAAW;AAAA,IAExC;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,aAAa,OAAO;AACpC,SAAO,MAAM;AACX,QAAI,OAAO,QAAQ,WAAW;AAAA,EAChC;AACF;AAEO,SAAS,kBAAkB,aAAiD;AACjF,SAAO,eAAe,EAAE,IAAI,WAAW;AACzC;AAEO,SAAS,sBAAwC;AACtD,SAAO,MAAM,KAAK,eAAe,EAAE,OAAO,CAAC;AAC7C;AAEO,SAAS,iCAA2C;AACzD,SAAO,MAAM,KAAK,eAAe,EAAE,KAAK,CAAC;AAC3C;AAEO,SAAS,kBAAkB,aAA8B;AAC9D,SAAO,eAAe,EAAE,IAAI,WAAW;AACzC;AAMO,SAAS,uBAA6B;AAC3C,iBAAe,EAAE,MAAM;AACzB;AAOO,MAAM,uBAAuB;AAAA,EAClC,SAAS,SAA+B;AACtC,2BAAuB,OAAO;AAAA,EAChC;AAAA,EAEA,IAAI,aAAiD;AACnD,WAAO,kBAAkB,WAAW;AAAA,EACtC;AAAA,EAEA,OAAyB;AACvB,WAAO,oBAAoB;AAAA,EAC7B;AAAA,EAEA,eAAyB;AACvB,WAAO,+BAA+B;AAAA,EACxC;AAAA,EAEA,IAAI,aAA8B;AAChC,WAAO,kBAAkB,WAAW;AAAA,EACtC;AAAA,EAEA,QAAc;AACZ,yBAAqB;AAAA,EACvB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runCrudMutationGuardAfterSuccess,
|
|
3
|
+
validateCrudMutationGuard
|
|
4
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
5
|
+
async function validateRouteMutationGuard(params) {
|
|
6
|
+
const { auth, container, input, req } = params;
|
|
7
|
+
if (!auth.sub || !auth.tenantId) {
|
|
8
|
+
return { result: null, afterSuccess: async () => void 0 };
|
|
9
|
+
}
|
|
10
|
+
const base = {
|
|
11
|
+
tenantId: auth.tenantId,
|
|
12
|
+
organizationId: auth.orgId ?? null,
|
|
13
|
+
userId: auth.sub,
|
|
14
|
+
resourceKind: input.resourceKind,
|
|
15
|
+
resourceId: input.resourceId,
|
|
16
|
+
operation: input.operation ?? "custom",
|
|
17
|
+
requestMethod: req.method,
|
|
18
|
+
requestHeaders: req.headers
|
|
19
|
+
};
|
|
20
|
+
const result = await validateCrudMutationGuard(container, {
|
|
21
|
+
...base,
|
|
22
|
+
mutationPayload: input.mutationPayload ?? null
|
|
23
|
+
});
|
|
24
|
+
if (result && !result.ok) {
|
|
25
|
+
return {
|
|
26
|
+
response: Response.json(result.body, { status: result.status })
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
result,
|
|
31
|
+
afterSuccess: async () => {
|
|
32
|
+
if (!result?.ok || !result.shouldRunAfterSuccess) return;
|
|
33
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
34
|
+
...base,
|
|
35
|
+
metadata: result.metadata ?? null
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
validateRouteMutationGuard
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=route-mutation-guard.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/communication_channels/lib/route-mutation-guard.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n type CrudMutationGuardValidationResult,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\ntype RouteAuth = {\n sub?: string | null\n tenantId?: string | null\n orgId?: string | null\n}\n\ntype RouteMutationGuardInput = {\n resourceKind: string\n resourceId: string\n operation?: 'create' | 'update' | 'delete' | 'custom'\n mutationPayload?: Record<string, unknown> | null\n}\n\nexport type RouteMutationGuardContext = {\n result: CrudMutationGuardValidationResult | null\n afterSuccess: () => Promise<void>\n}\n\nexport async function validateRouteMutationGuard(params: {\n container: AwilixContainer\n req: Request\n auth: RouteAuth\n input: RouteMutationGuardInput\n}): Promise<RouteMutationGuardContext | { response: Response }> {\n const { auth, container, input, req } = params\n if (!auth.sub || !auth.tenantId) {\n return { result: null, afterSuccess: async () => undefined }\n }\n\n const base = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId ?? null,\n userId: auth.sub,\n resourceKind: input.resourceKind,\n resourceId: input.resourceId,\n operation: input.operation ?? 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n } as const\n\n const result = await validateCrudMutationGuard(container, {\n ...base,\n mutationPayload: input.mutationPayload ?? null,\n })\n if (result && !result.ok) {\n return {\n response: Response.json(result.body, { status: result.status }),\n }\n }\n\n return {\n result,\n afterSuccess: async () => {\n if (!result?.ok || !result.shouldRunAfterSuccess) return\n await runCrudMutationGuardAfterSuccess(container, {\n ...base,\n metadata: result.metadata ?? null,\n })\n },\n }\n}\n"],
|
|
5
|
+
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAoBP,eAAsB,2BAA2B,QAKe;AAC9D,QAAM,EAAE,MAAM,WAAW,OAAO,IAAI,IAAI;AACxC,MAAI,CAAC,KAAK,OAAO,CAAC,KAAK,UAAU;AAC/B,WAAO,EAAE,QAAQ,MAAM,cAAc,YAAY,OAAU;AAAA,EAC7D;AAEA,QAAM,OAAO;AAAA,IACX,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK,SAAS;AAAA,IAC9B,QAAQ,KAAK;AAAA,IACb,cAAc,MAAM;AAAA,IACpB,YAAY,MAAM;AAAA,IAClB,WAAW,MAAM,aAAa;AAAA,IAC9B,eAAe,IAAI;AAAA,IACnB,gBAAgB,IAAI;AAAA,EACtB;AAEA,QAAM,SAAS,MAAM,0BAA0B,WAAW;AAAA,IACxD,GAAG;AAAA,IACH,iBAAiB,MAAM,mBAAmB;AAAA,EAC5C,CAAC;AACD,MAAI,UAAU,CAAC,OAAO,IAAI;AACxB,WAAO;AAAA,MACL,UAAU,SAAS,KAAK,OAAO,MAAM,EAAE,QAAQ,OAAO,OAAO,CAAC;AAAA,IAChE;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,cAAc,YAAY;AACxB,UAAI,CAAC,QAAQ,MAAM,CAAC,OAAO,sBAAuB;AAClD,YAAM,iCAAiC,WAAW;AAAA,QAChD,GAAG;AAAA,QACH,UAAU,OAAO,YAAY;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import sanitizeHtml from "sanitize-html";
|
|
2
|
+
const ALLOWED_TAGS = [
|
|
3
|
+
"a",
|
|
4
|
+
"img",
|
|
5
|
+
"table",
|
|
6
|
+
"thead",
|
|
7
|
+
"tbody",
|
|
8
|
+
"tfoot",
|
|
9
|
+
"tr",
|
|
10
|
+
"td",
|
|
11
|
+
"th",
|
|
12
|
+
"p",
|
|
13
|
+
"br",
|
|
14
|
+
"hr",
|
|
15
|
+
"ul",
|
|
16
|
+
"ol",
|
|
17
|
+
"li",
|
|
18
|
+
"h1",
|
|
19
|
+
"h2",
|
|
20
|
+
"h3",
|
|
21
|
+
"h4",
|
|
22
|
+
"h5",
|
|
23
|
+
"h6",
|
|
24
|
+
"strong",
|
|
25
|
+
"em",
|
|
26
|
+
"b",
|
|
27
|
+
"i",
|
|
28
|
+
"u",
|
|
29
|
+
"s",
|
|
30
|
+
"blockquote",
|
|
31
|
+
"code",
|
|
32
|
+
"pre",
|
|
33
|
+
"span",
|
|
34
|
+
"div"
|
|
35
|
+
];
|
|
36
|
+
const ALLOWED_SCHEMES = ["http", "https", "mailto", "tel"];
|
|
37
|
+
const ALLOWED_SCHEMES_BY_TAG = {
|
|
38
|
+
img: ["http", "https", "data"]
|
|
39
|
+
};
|
|
40
|
+
const SAFE_CSS_COLOR = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$|^rgba?\(\s*[\d.,\s%]+\)$|^hsla?\(\s*[\d.,\s%]+\)$|^[a-z]+$/i;
|
|
41
|
+
const SANITIZE_OPTIONS = {
|
|
42
|
+
allowedTags: ALLOWED_TAGS,
|
|
43
|
+
allowedAttributes: {
|
|
44
|
+
a: ["href", "title", "class"],
|
|
45
|
+
img: ["src", "alt", "title", "class", "width", "height"],
|
|
46
|
+
"*": ["class", "style", "colspan", "rowspan", "width", "height"]
|
|
47
|
+
},
|
|
48
|
+
allowedSchemes: ALLOWED_SCHEMES,
|
|
49
|
+
allowedSchemesByTag: ALLOWED_SCHEMES_BY_TAG,
|
|
50
|
+
allowedSchemesAppliedToAttributes: ["href", "src"],
|
|
51
|
+
allowProtocolRelative: false,
|
|
52
|
+
// disable inline scripts in style attributes
|
|
53
|
+
allowedStyles: {
|
|
54
|
+
"*": {
|
|
55
|
+
"background-color": [SAFE_CSS_COLOR],
|
|
56
|
+
color: [SAFE_CSS_COLOR],
|
|
57
|
+
"text-align": [/^left$|^right$|^center$|^justify$/i],
|
|
58
|
+
"font-size": [/^\d+(?:\.\d+)?(?:px|em|rem|%)$/],
|
|
59
|
+
"font-weight": [/^\d{3}$|^bold$|^normal$/],
|
|
60
|
+
"font-style": [/^italic$|^normal$/],
|
|
61
|
+
"text-decoration": [/^underline$|^line-through$|^none$/],
|
|
62
|
+
padding: [/^[\d\s.]+(?:px|em|rem|%)?$/],
|
|
63
|
+
margin: [/^[\d\s.]+(?:px|em|rem|%)?$/],
|
|
64
|
+
border: [/^[\d\s\w.]+$/],
|
|
65
|
+
"border-radius": [/^\d+(?:\.\d+)?(?:px|em|rem|%)$/],
|
|
66
|
+
// Permit only `display:none` so the hidden thread-token footer span
|
|
67
|
+
// (`buildBodyFooter`) stays hidden when a sent email body is re-rendered.
|
|
68
|
+
// Every other display value is stripped.
|
|
69
|
+
display: [/^none$/]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* data:image/<mime>;base64,... URLs in <img src=...> are explicitly allowed so inline
|
|
74
|
+
* email images render. sanitize-html validates the base64 payload shape; if a future
|
|
75
|
+
* payload tries to smuggle a non-image MIME type via data URL we strip it.
|
|
76
|
+
*/
|
|
77
|
+
exclusiveFilter: (frame) => {
|
|
78
|
+
if (frame.tag === "img" && frame.attribs?.src) {
|
|
79
|
+
const src = frame.attribs.src;
|
|
80
|
+
if (src.startsWith("data:") && !/^data:image\/(?:png|jpe?g|gif|webp);base64,/i.test(src)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
function sanitizeChannelHtml(html) {
|
|
88
|
+
if (!html) return "";
|
|
89
|
+
return sanitizeHtml(html, SANITIZE_OPTIONS);
|
|
90
|
+
}
|
|
91
|
+
var sanitize_channel_html_default = sanitizeChannelHtml;
|
|
92
|
+
export {
|
|
93
|
+
sanitize_channel_html_default as default,
|
|
94
|
+
sanitizeChannelHtml
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=sanitize-channel-html.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/communication_channels/lib/sanitize-channel-html.ts"],
|
|
4
|
+
"sourcesContent": ["import sanitizeHtml from 'sanitize-html'\n\n/**\n * HTML sanitizer for channel-supplied payloads (email MIME, Slack rich-text, future channel types).\n *\n * The hub owns this helper; every widget that renders channel HTML \u2014 in the hub itself or in\n * downstream provider packages \u2014 imports it. The Messages module's `channel-payload-renderer`\n * widget calls this before any `dangerouslySetInnerHTML`-style render.\n *\n * Implementation note: backed by `sanitize-html` (CommonJS, server-friendly) rather than\n * DOMPurify so the same code runs in Jest without ESM transform gymnastics. SPEC-045d \u00A74.6\n * specifies \"DOMPurify or equivalent\" \u2014 `sanitize-html` is an allowlist-based equivalent\n * widely used for the same sanitization shape.\n *\n * Allowlist is tuned for email + chat HTML:\n * - Preserves typical email layout primitives (table-based layouts, inline images, basic typography).\n * - Strips `<script>`, `<style>`, `<iframe>`, all event-handler attributes (`on*`).\n * - Blocks `javascript:` and `data:` URLs except `data:image/*;base64,\u2026` (inline base64 images, common in email).\n */\n\nconst ALLOWED_TAGS = [\n 'a',\n 'img',\n 'table',\n 'thead',\n 'tbody',\n 'tfoot',\n 'tr',\n 'td',\n 'th',\n 'p',\n 'br',\n 'hr',\n 'ul',\n 'ol',\n 'li',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'strong',\n 'em',\n 'b',\n 'i',\n 'u',\n 's',\n 'blockquote',\n 'code',\n 'pre',\n 'span',\n 'div',\n]\n\nconst ALLOWED_SCHEMES = ['http', 'https', 'mailto', 'tel']\nconst ALLOWED_SCHEMES_BY_TAG = {\n img: ['http', 'https', 'data'],\n}\n\n/**\n * Safe CSS color value shapes only: hex (#rgb / #rgba / #rrggbb / #rrggbbaa),\n * rgb()/rgba(), hsl()/hsla(), and bare named colors (`red`, `transparent`,\n * `currentcolor`, \u2026). Deliberately rejects any value containing `url(` or\n * `expression(` so a `color`/`background-color` declaration can never smuggle a\n * CSS-based tracking beacon or legacy-IE script expression past the sanitizer.\n */\nconst SAFE_CSS_COLOR =\n /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$|^rgba?\\(\\s*[\\d.,\\s%]+\\)$|^hsla?\\(\\s*[\\d.,\\s%]+\\)$|^[a-z]+$/i\n\nconst SANITIZE_OPTIONS: sanitizeHtml.IOptions = {\n allowedTags: ALLOWED_TAGS,\n allowedAttributes: {\n a: ['href', 'title', 'class'],\n img: ['src', 'alt', 'title', 'class', 'width', 'height'],\n '*': ['class', 'style', 'colspan', 'rowspan', 'width', 'height'],\n },\n allowedSchemes: ALLOWED_SCHEMES,\n allowedSchemesByTag: ALLOWED_SCHEMES_BY_TAG,\n allowedSchemesAppliedToAttributes: ['href', 'src'],\n allowProtocolRelative: false,\n // disable inline scripts in style attributes\n allowedStyles: {\n '*': {\n 'background-color': [SAFE_CSS_COLOR],\n color: [SAFE_CSS_COLOR],\n 'text-align': [/^left$|^right$|^center$|^justify$/i],\n 'font-size': [/^\\d+(?:\\.\\d+)?(?:px|em|rem|%)$/],\n 'font-weight': [/^\\d{3}$|^bold$|^normal$/],\n 'font-style': [/^italic$|^normal$/],\n 'text-decoration': [/^underline$|^line-through$|^none$/],\n padding: [/^[\\d\\s.]+(?:px|em|rem|%)?$/],\n margin: [/^[\\d\\s.]+(?:px|em|rem|%)?$/],\n border: [/^[\\d\\s\\w.]+$/],\n 'border-radius': [/^\\d+(?:\\.\\d+)?(?:px|em|rem|%)$/],\n // Permit only `display:none` so the hidden thread-token footer span\n // (`buildBodyFooter`) stays hidden when a sent email body is re-rendered.\n // Every other display value is stripped.\n display: [/^none$/],\n },\n },\n /**\n * data:image/<mime>;base64,... URLs in <img src=...> are explicitly allowed so inline\n * email images render. sanitize-html validates the base64 payload shape; if a future\n * payload tries to smuggle a non-image MIME type via data URL we strip it.\n */\n exclusiveFilter: (frame) => {\n if (frame.tag === 'img' && frame.attribs?.src) {\n const src = frame.attribs.src\n if (src.startsWith('data:') && !/^data:image\\/(?:png|jpe?g|gif|webp);base64,/i.test(src)) {\n return true\n }\n }\n return false\n },\n}\n\n/**\n * Sanitize an HTML string for safe rendering.\n *\n * @param html Raw HTML \u2014 may originate from an external channel (email body, Slack mrkdwn rendered to HTML, \u2026).\n * @returns Sanitized HTML safe for `dangerouslySetInnerHTML`.\n */\nexport function sanitizeChannelHtml(html: string): string {\n if (!html) return ''\n return sanitizeHtml(html, SANITIZE_OPTIONS)\n}\n\nexport default sanitizeChannelHtml\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,kBAAkB;AAoBzB,MAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,kBAAkB,CAAC,QAAQ,SAAS,UAAU,KAAK;AACzD,MAAM,yBAAyB;AAAA,EAC7B,KAAK,CAAC,QAAQ,SAAS,MAAM;AAC/B;AASA,MAAM,iBACJ;AAEF,MAAM,mBAA0C;AAAA,EAC9C,aAAa;AAAA,EACb,mBAAmB;AAAA,IACjB,GAAG,CAAC,QAAQ,SAAS,OAAO;AAAA,IAC5B,KAAK,CAAC,OAAO,OAAO,SAAS,SAAS,SAAS,QAAQ;AAAA,IACvD,KAAK,CAAC,SAAS,SAAS,WAAW,WAAW,SAAS,QAAQ;AAAA,EACjE;AAAA,EACA,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,mCAAmC,CAAC,QAAQ,KAAK;AAAA,EACjD,uBAAuB;AAAA;AAAA,EAEvB,eAAe;AAAA,IACb,KAAK;AAAA,MACH,oBAAoB,CAAC,cAAc;AAAA,MACnC,OAAO,CAAC,cAAc;AAAA,MACtB,cAAc,CAAC,oCAAoC;AAAA,MACnD,aAAa,CAAC,gCAAgC;AAAA,MAC9C,eAAe,CAAC,yBAAyB;AAAA,MACzC,cAAc,CAAC,mBAAmB;AAAA,MAClC,mBAAmB,CAAC,mCAAmC;AAAA,MACvD,SAAS,CAAC,4BAA4B;AAAA,MACtC,QAAQ,CAAC,4BAA4B;AAAA,MACrC,QAAQ,CAAC,cAAc;AAAA,MACvB,iBAAiB,CAAC,gCAAgC;AAAA;AAAA;AAAA;AAAA,MAIlD,SAAS,CAAC,QAAQ;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,CAAC,UAAU;AAC1B,QAAI,MAAM,QAAQ,SAAS,MAAM,SAAS,KAAK;AAC7C,YAAM,MAAM,MAAM,QAAQ;AAC1B,UAAI,IAAI,WAAW,OAAO,KAAK,CAAC,+CAA+C,KAAK,GAAG,GAAG;AACxF,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;AAQO,SAAS,oBAAoB,MAAsB;AACxD,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,aAAa,MAAM,gBAAgB;AAC5C;AAEA,IAAO,gCAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
2
|
+
import {
|
|
3
|
+
ChannelThreadMapping,
|
|
4
|
+
CommunicationChannel,
|
|
5
|
+
ExternalConversation,
|
|
6
|
+
MessageChannelLink
|
|
7
|
+
} from "../data/entities.js";
|
|
8
|
+
import { ChannelMutationBlockedError, guardOutboundCreate } from "./mutation-guards.js";
|
|
9
|
+
import { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from "./queue.js";
|
|
10
|
+
import { htmlToText } from "./email-mime.js";
|
|
11
|
+
async function sendAsUser(container, actor, input) {
|
|
12
|
+
if (!input.body.plain && !input.body.html) {
|
|
13
|
+
return { ok: false, status: 422, error: "Either body.plain or body.html is required" };
|
|
14
|
+
}
|
|
15
|
+
if (!Array.isArray(input.to) || input.to.length === 0) {
|
|
16
|
+
return { ok: false, status: 422, error: "At least one recipient is required" };
|
|
17
|
+
}
|
|
18
|
+
const em = container.resolve("em").fork();
|
|
19
|
+
const { tenantId } = actor;
|
|
20
|
+
const organizationId = actor.organizationId;
|
|
21
|
+
const dscope = { tenantId, organizationId };
|
|
22
|
+
const channel = await findOneWithDecryption(
|
|
23
|
+
em,
|
|
24
|
+
CommunicationChannel,
|
|
25
|
+
{ id: input.userChannelId, tenantId, organizationId, deletedAt: null },
|
|
26
|
+
void 0,
|
|
27
|
+
dscope
|
|
28
|
+
);
|
|
29
|
+
if (!channel) return { ok: false, status: 404, error: "Channel not found" };
|
|
30
|
+
if (channel.userId !== actor.userId) {
|
|
31
|
+
return { ok: false, status: 403, error: "You can only send through channels you own" };
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
await guardOutboundCreate(em, { channelId: channel.id, scope: dscope });
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (err instanceof ChannelMutationBlockedError) {
|
|
37
|
+
return { ok: false, status: 422, error: err.message, fieldErrors: err.errors };
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
if (!channel.isActive || channel.status !== "connected") {
|
|
42
|
+
return {
|
|
43
|
+
ok: false,
|
|
44
|
+
status: 409,
|
|
45
|
+
error: `Channel is in status '${channel.status}' (not connected)`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const commandBus = container.resolve("commandBus");
|
|
49
|
+
const messageBody = input.body.plain ?? htmlToText(input.body.html ?? "");
|
|
50
|
+
const composeInput = {
|
|
51
|
+
type: `channel.${channel.providerKey}`,
|
|
52
|
+
visibility: "public",
|
|
53
|
+
sourceEntityType: "communication_channels.send_as_user",
|
|
54
|
+
sourceEntityId: channel.id,
|
|
55
|
+
externalEmail: input.to[0],
|
|
56
|
+
externalName: input.subject,
|
|
57
|
+
recipients: [],
|
|
58
|
+
subject: input.subject,
|
|
59
|
+
body: messageBody,
|
|
60
|
+
bodyFormat: "text",
|
|
61
|
+
priority: "normal",
|
|
62
|
+
sendViaEmail: false,
|
|
63
|
+
parentMessageId: input.parentMessageId,
|
|
64
|
+
isDraft: false,
|
|
65
|
+
tenantId,
|
|
66
|
+
organizationId,
|
|
67
|
+
userId: actor.userId
|
|
68
|
+
};
|
|
69
|
+
let result;
|
|
70
|
+
try {
|
|
71
|
+
result = await commandBus.execute(
|
|
72
|
+
"messages.messages.compose",
|
|
73
|
+
{
|
|
74
|
+
input: composeInput,
|
|
75
|
+
ctx: {
|
|
76
|
+
container,
|
|
77
|
+
auth: actor.auth,
|
|
78
|
+
organizationScope: null,
|
|
79
|
+
selectedOrganizationId: organizationId,
|
|
80
|
+
organizationIds: organizationId ? [organizationId] : null
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error("[communication_channels] send-as-user compose failed", err);
|
|
86
|
+
return { ok: false, status: 500, error: "[internal] compose failed" };
|
|
87
|
+
}
|
|
88
|
+
const messageId = result.result.id;
|
|
89
|
+
const messageThreadId = result.result.threadId ?? messageId;
|
|
90
|
+
const externalThreadRef = `outbound:${messageThreadId}`;
|
|
91
|
+
await em.transactional(async () => {
|
|
92
|
+
let conversation = await findOneWithDecryption(
|
|
93
|
+
em,
|
|
94
|
+
ExternalConversation,
|
|
95
|
+
{
|
|
96
|
+
channelId: channel.id,
|
|
97
|
+
externalConversationId: externalThreadRef,
|
|
98
|
+
tenantId,
|
|
99
|
+
organizationId
|
|
100
|
+
},
|
|
101
|
+
void 0,
|
|
102
|
+
dscope
|
|
103
|
+
);
|
|
104
|
+
if (!conversation) {
|
|
105
|
+
conversation = em.create(ExternalConversation, {
|
|
106
|
+
channelId: channel.id,
|
|
107
|
+
externalConversationId: externalThreadRef,
|
|
108
|
+
subject: input.subject,
|
|
109
|
+
assignedUserId: actor.userId,
|
|
110
|
+
tenantId,
|
|
111
|
+
organizationId,
|
|
112
|
+
lastMessageAt: /* @__PURE__ */ new Date()
|
|
113
|
+
});
|
|
114
|
+
em.persist(conversation);
|
|
115
|
+
await em.flush();
|
|
116
|
+
} else {
|
|
117
|
+
conversation.subject = conversation.subject ?? input.subject;
|
|
118
|
+
conversation.assignedUserId = conversation.assignedUserId ?? actor.userId;
|
|
119
|
+
conversation.lastMessageAt = /* @__PURE__ */ new Date();
|
|
120
|
+
await em.flush();
|
|
121
|
+
}
|
|
122
|
+
const existingMapping = await findOneWithDecryption(
|
|
123
|
+
em,
|
|
124
|
+
ChannelThreadMapping,
|
|
125
|
+
{
|
|
126
|
+
externalConversationId: conversation.id,
|
|
127
|
+
tenantId,
|
|
128
|
+
organizationId
|
|
129
|
+
},
|
|
130
|
+
void 0,
|
|
131
|
+
dscope
|
|
132
|
+
);
|
|
133
|
+
if (!existingMapping) {
|
|
134
|
+
const mapping = em.create(ChannelThreadMapping, {
|
|
135
|
+
externalConversationId: conversation.id,
|
|
136
|
+
messageThreadId,
|
|
137
|
+
channelId: channel.id,
|
|
138
|
+
providerKey: channel.providerKey,
|
|
139
|
+
externalThreadRef,
|
|
140
|
+
assignedUserId: actor.userId,
|
|
141
|
+
tenantId,
|
|
142
|
+
organizationId
|
|
143
|
+
});
|
|
144
|
+
em.persist(mapping);
|
|
145
|
+
}
|
|
146
|
+
const channelLink = em.create(MessageChannelLink, {
|
|
147
|
+
messageId,
|
|
148
|
+
externalConversationId: conversation.id,
|
|
149
|
+
providerKey: channel.providerKey,
|
|
150
|
+
channelType: channel.channelType,
|
|
151
|
+
direction: "outbound",
|
|
152
|
+
deliveryStatus: "pending",
|
|
153
|
+
channelPayload: {
|
|
154
|
+
text: input.body.plain ?? messageBody,
|
|
155
|
+
...input.body.html ? { html: input.body.html } : {}
|
|
156
|
+
},
|
|
157
|
+
channelContentType: input.body.html ? "text/html" : "text/plain",
|
|
158
|
+
channelMetadata: {
|
|
159
|
+
// Caller-supplied pass-through metadata merged FIRST so the validated
|
|
160
|
+
// routing fields below always win — a caller cannot override the
|
|
161
|
+
// recipients/subject/threading headers via `channelMetadata`.
|
|
162
|
+
...input.channelMetadata ?? {},
|
|
163
|
+
to: input.to,
|
|
164
|
+
cc: input.cc ?? [],
|
|
165
|
+
bcc: input.bcc ?? [],
|
|
166
|
+
subject: input.subject,
|
|
167
|
+
inReplyTo: input.inReplyTo ?? null,
|
|
168
|
+
references: input.references ?? []
|
|
169
|
+
},
|
|
170
|
+
tenantId,
|
|
171
|
+
organizationId
|
|
172
|
+
});
|
|
173
|
+
em.persist(channelLink);
|
|
174
|
+
await em.flush();
|
|
175
|
+
});
|
|
176
|
+
const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.outbound);
|
|
177
|
+
const deliveryJob = {
|
|
178
|
+
messageId,
|
|
179
|
+
scope: dscope,
|
|
180
|
+
attempt: 1
|
|
181
|
+
};
|
|
182
|
+
await queue.enqueue(deliveryJob);
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
messageId,
|
|
186
|
+
threadId: messageThreadId,
|
|
187
|
+
channelId: channel.id,
|
|
188
|
+
providerKey: channel.providerKey
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
export {
|
|
192
|
+
sendAsUser
|
|
193
|
+
};
|
|
194
|
+
//# sourceMappingURL=send-as-user.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/communication_channels/lib/send-as-user.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n ChannelThreadMapping,\n CommunicationChannel,\n ExternalConversation,\n MessageChannelLink,\n} from '../data/entities'\nimport { ChannelMutationBlockedError, guardOutboundCreate } from './mutation-guards'\nimport { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from './queue'\nimport type { OutboundDeliveryPayload } from '../workers/outbound-delivery'\nimport { htmlToText } from './email-mime'\n\nexport type SendAsUserActor = {\n userId: string\n tenantId: string\n organizationId: string | null\n /** Forwarded as the messages compose command `ctx.auth`. */\n auth?: unknown\n}\n\nexport type SendAsUserInput = {\n userChannelId: string\n to: string[]\n cc?: string[]\n bcc?: string[]\n subject: string\n body: { plain?: string; html?: string }\n inReplyTo?: string\n references?: string[]\n /**\n * Open Mercato `messages.message` id of the message being replied to. When\n * set, the composed message joins that message's thread (the messages module\n * derives `threadId` from the parent), so a CRM reply continues the existing\n * conversation instead of starting a new thread. Optional \u2014 omitted for new\n * threads.\n */\n parentMessageId?: string\n /**\n * Free-form metadata persisted on the resulting MessageChannelLink. Used by\n * downstream subscribers (e.g. the customers module's link-channel-message\n * subscriber) to anchor the sent message back to a CRM Person or honor a\n * caller-specified visibility flag. The hub does not interpret these keys.\n */\n channelMetadata?: Record<string, unknown>\n}\n\nexport type SendAsUserResult =\n | {\n ok: true\n messageId: string\n threadId: string\n channelId: string\n providerKey: string\n }\n | { ok: false; status: number; error: string; fieldErrors?: Record<string, string> }\n\n/**\n * In-process send-as-user facade.\n *\n * Validates the actor owns `userChannelId`, composes the Message via the\n * messages module command, persists the outbound MessageChannelLink + thread\n * mapping, and enqueues delivery. Returns a discriminated result instead of an\n * HTTP Response so it can be invoked both by the `/send-as-user` route and by\n * other modules via DI (`container.resolve('communicationChannelsSendAsUser')`)\n * \u2014 no HTTP self-call required.\n */\nexport async function sendAsUser(\n container: AppContainer,\n actor: SendAsUserActor,\n input: SendAsUserInput,\n): Promise<SendAsUserResult> {\n if (!input.body.plain && !input.body.html) {\n return { ok: false, status: 422, error: 'Either body.plain or body.html is required' }\n }\n // Re-validate here (not only in the HTTP route's zod schema) because this\n // facade is also reachable via DI; `input.to[0]` below would otherwise become\n // `undefined` for an empty recipient list.\n if (!Array.isArray(input.to) || input.to.length === 0) {\n return { ok: false, status: 422, error: 'At least one recipient is required' }\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const { tenantId } = actor\n const organizationId = actor.organizationId\n const dscope = { tenantId, organizationId }\n\n const channel = await findOneWithDecryption(\n em,\n CommunicationChannel,\n { id: input.userChannelId, tenantId, organizationId, deletedAt: null },\n undefined,\n dscope,\n )\n if (!channel) return { ok: false, status: 404, error: 'Channel not found' }\n if (channel.userId !== actor.userId) {\n return { ok: false, status: 403, error: 'You can only send through channels you own' }\n }\n\n // Hub mutation guard: map known non-deliverable states (`requires_reauth`,\n // `disconnected`) to a 422 with field-level errors; other transitional states\n // fall through to the 409 below.\n try {\n await guardOutboundCreate(em, { channelId: channel.id, scope: dscope })\n } catch (err) {\n if (err instanceof ChannelMutationBlockedError) {\n return { ok: false, status: 422, error: err.message, fieldErrors: err.errors }\n }\n throw err\n }\n if (!channel.isActive || channel.status !== 'connected') {\n return {\n ok: false,\n status: 409,\n error: `Channel is in status '${channel.status}' (not connected)`,\n }\n }\n\n // Create the Message via the messages module compose command. The outbound\n // subscriber picks it up via `messages.message.sent` and routes through the\n // adapter chain.\n const commandBus = container.resolve('commandBus') as CommandBus\n const messageBody = input.body.plain ?? htmlToText(input.body.html ?? '')\n const composeInput = {\n type: `channel.${channel.providerKey}`,\n visibility: 'public' as const,\n sourceEntityType: 'communication_channels.send_as_user',\n sourceEntityId: channel.id,\n externalEmail: input.to[0],\n externalName: input.subject,\n recipients: [],\n subject: input.subject,\n body: messageBody,\n bodyFormat: 'text' as const,\n priority: 'normal' as const,\n sendViaEmail: false,\n parentMessageId: input.parentMessageId,\n isDraft: false,\n tenantId,\n organizationId,\n userId: actor.userId,\n }\n let result\n try {\n result = await commandBus.execute<typeof composeInput, { id: string; threadId: string | null }>(\n 'messages.messages.compose',\n {\n input: composeInput,\n ctx: {\n container,\n auth: actor.auth as never,\n organizationScope: null,\n selectedOrganizationId: organizationId,\n organizationIds: organizationId ? [organizationId] : null,\n },\n },\n )\n } catch (err) {\n console.error('[communication_channels] send-as-user compose failed', err)\n return { ok: false, status: 500, error: '[internal] compose failed' }\n }\n\n const messageId = result.result.id\n const messageThreadId = result.result.threadId ?? messageId\n const externalThreadRef = `outbound:${messageThreadId}`\n\n // Persist the conversation, thread mapping, and channel link as one unit. The\n // conversation's id is DB-generated (`gen_random_uuid()`), so it must be\n // flushed before the mapping/link can reference it \u2014 hence the eager flushes\n // rather than `withAtomicFlush` (single trailing flush). `em.transactional`\n // wraps the whole sequence so a mid-way failure rolls back all three writes\n // instead of leaving an orphaned conversation row.\n await em.transactional(async () => {\n let conversation = await findOneWithDecryption(\n em,\n ExternalConversation,\n {\n channelId: channel.id,\n externalConversationId: externalThreadRef,\n tenantId,\n organizationId,\n },\n undefined,\n dscope,\n )\n if (!conversation) {\n conversation = em.create(ExternalConversation, {\n channelId: channel.id,\n externalConversationId: externalThreadRef,\n subject: input.subject,\n assignedUserId: actor.userId,\n tenantId,\n organizationId,\n lastMessageAt: new Date(),\n })\n em.persist(conversation)\n await em.flush()\n } else {\n conversation.subject = conversation.subject ?? input.subject\n conversation.assignedUserId = conversation.assignedUserId ?? actor.userId\n conversation.lastMessageAt = new Date()\n // Flush the scalar updates BEFORE the ChannelThreadMapping lookup below: a\n // find/findOne on the same EntityManager between a scalar mutation and flush\n // can silently drop the pending UPDATE (core AGENTS.md \u2192 Entity Update Safety).\n await em.flush()\n }\n\n const existingMapping = await findOneWithDecryption(\n em,\n ChannelThreadMapping,\n {\n externalConversationId: conversation.id,\n tenantId,\n organizationId,\n },\n undefined,\n dscope,\n )\n if (!existingMapping) {\n const mapping = em.create(ChannelThreadMapping, {\n externalConversationId: conversation.id,\n messageThreadId,\n channelId: channel.id,\n providerKey: channel.providerKey,\n externalThreadRef,\n assignedUserId: actor.userId,\n tenantId,\n organizationId,\n })\n em.persist(mapping)\n }\n\n const channelLink = em.create(MessageChannelLink, {\n messageId,\n externalConversationId: conversation.id,\n providerKey: channel.providerKey,\n channelType: channel.channelType,\n direction: 'outbound',\n deliveryStatus: 'pending',\n channelPayload: {\n text: input.body.plain ?? messageBody,\n ...(input.body.html ? { html: input.body.html } : {}),\n },\n channelContentType: input.body.html ? 'text/html' : 'text/plain',\n channelMetadata: {\n // Caller-supplied pass-through metadata merged FIRST so the validated\n // routing fields below always win \u2014 a caller cannot override the\n // recipients/subject/threading headers via `channelMetadata`.\n ...(input.channelMetadata ?? {}),\n to: input.to,\n cc: input.cc ?? [],\n bcc: input.bcc ?? [],\n subject: input.subject,\n inReplyTo: input.inReplyTo ?? null,\n references: input.references ?? [],\n },\n tenantId,\n organizationId,\n })\n em.persist(channelLink)\n await em.flush()\n })\n\n const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.outbound)\n const deliveryJob: OutboundDeliveryPayload = {\n messageId,\n scope: dscope,\n attempt: 1,\n }\n await queue.enqueue(deliveryJob as unknown as Record<string, unknown>)\n\n return {\n ok: true,\n messageId,\n threadId: messageThreadId,\n channelId: channel.id,\n providerKey: channel.providerKey,\n }\n}\n\n/** DI service type for cross-module callers (resolve `communicationChannelsSendAsUser`). */\nexport type SendAsUserService = typeof sendAsUser\n"],
|
|
5
|
+
"mappings": "AAGA,SAAS,6BAA6B;AACtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B,2BAA2B;AACjE,SAAS,+BAA+B,qCAAqC;AAE7E,SAAS,kBAAkB;AAwD3B,eAAsB,WACpB,WACA,OACA,OAC2B;AAC3B,MAAI,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,KAAK,MAAM;AACzC,WAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,6CAA6C;AAAA,EACvF;AAIA,MAAI,CAAC,MAAM,QAAQ,MAAM,EAAE,KAAK,MAAM,GAAG,WAAW,GAAG;AACrD,WAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,qCAAqC;AAAA,EAC/E;AAEA,QAAM,KAAM,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC3D,QAAM,EAAE,SAAS,IAAI;AACrB,QAAM,iBAAiB,MAAM;AAC7B,QAAM,SAAS,EAAE,UAAU,eAAe;AAE1C,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,MAAM,eAAe,UAAU,gBAAgB,WAAW,KAAK;AAAA,IACrE;AAAA,IACA;AAAA,EACF;AACA,MAAI,CAAC,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,oBAAoB;AAC1E,MAAI,QAAQ,WAAW,MAAM,QAAQ;AACnC,WAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,6CAA6C;AAAA,EACvF;AAKA,MAAI;AACF,UAAM,oBAAoB,IAAI,EAAE,WAAW,QAAQ,IAAI,OAAO,OAAO,CAAC;AAAA,EACxE,SAAS,KAAK;AACZ,QAAI,eAAe,6BAA6B;AAC9C,aAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,IAAI,SAAS,aAAa,IAAI,OAAO;AAAA,IAC/E;AACA,UAAM;AAAA,EACR;AACA,MAAI,CAAC,QAAQ,YAAY,QAAQ,WAAW,aAAa;AACvD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,OAAO,yBAAyB,QAAQ,MAAM;AAAA,IAChD;AAAA,EACF;AAKA,QAAM,aAAa,UAAU,QAAQ,YAAY;AACjD,QAAM,cAAc,MAAM,KAAK,SAAS,WAAW,MAAM,KAAK,QAAQ,EAAE;AACxE,QAAM,eAAe;AAAA,IACnB,MAAM,WAAW,QAAQ,WAAW;AAAA,IACpC,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,gBAAgB,QAAQ;AAAA,IACxB,eAAe,MAAM,GAAG,CAAC;AAAA,IACzB,cAAc,MAAM;AAAA,IACpB,YAAY,CAAC;AAAA,IACb,SAAS,MAAM;AAAA,IACf,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,cAAc;AAAA,IACd,iBAAiB,MAAM;AAAA,IACvB,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,QAAQ,MAAM;AAAA,EAChB;AACA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,WAAW;AAAA,MACxB;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,KAAK;AAAA,UACH;AAAA,UACA,MAAM,MAAM;AAAA,UACZ,mBAAmB;AAAA,UACnB,wBAAwB;AAAA,UACxB,iBAAiB,iBAAiB,CAAC,cAAc,IAAI;AAAA,QACvD;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,wDAAwD,GAAG;AACzE,WAAO,EAAE,IAAI,OAAO,QAAQ,KAAK,OAAO,4BAA4B;AAAA,EACtE;AAEA,QAAM,YAAY,OAAO,OAAO;AAChC,QAAM,kBAAkB,OAAO,OAAO,YAAY;AAClD,QAAM,oBAAoB,YAAY,eAAe;AAQrD,QAAM,GAAG,cAAc,YAAY;AACjC,QAAI,eAAe,MAAM;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,QACE,WAAW,QAAQ;AAAA,QACnB,wBAAwB;AAAA,QACxB;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,cAAc;AACjB,qBAAe,GAAG,OAAO,sBAAsB;AAAA,QAC7C,WAAW,QAAQ;AAAA,QACnB,wBAAwB;AAAA,QACxB,SAAS,MAAM;AAAA,QACf,gBAAgB,MAAM;AAAA,QACtB;AAAA,QACA;AAAA,QACA,eAAe,oBAAI,KAAK;AAAA,MAC1B,CAAC;AACD,SAAG,QAAQ,YAAY;AACvB,YAAM,GAAG,MAAM;AAAA,IACjB,OAAO;AACL,mBAAa,UAAU,aAAa,WAAW,MAAM;AACrD,mBAAa,iBAAiB,aAAa,kBAAkB,MAAM;AACnE,mBAAa,gBAAgB,oBAAI,KAAK;AAItC,YAAM,GAAG,MAAM;AAAA,IACjB;AAEA,UAAM,kBAAkB,MAAM;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,QACE,wBAAwB,aAAa;AAAA,QACrC;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,iBAAiB;AACpB,YAAM,UAAU,GAAG,OAAO,sBAAsB;AAAA,QAC9C,wBAAwB,aAAa;AAAA,QACrC;AAAA,QACA,WAAW,QAAQ;AAAA,QACnB,aAAa,QAAQ;AAAA,QACrB;AAAA,QACA,gBAAgB,MAAM;AAAA,QACtB;AAAA,QACA;AAAA,MACF,CAAC;AACD,SAAG,QAAQ,OAAO;AAAA,IACpB;AAEA,UAAM,cAAc,GAAG,OAAO,oBAAoB;AAAA,MAChD;AAAA,MACA,wBAAwB,aAAa;AAAA,MACrC,aAAa,QAAQ;AAAA,MACrB,aAAa,QAAQ;AAAA,MACrB,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,QACd,MAAM,MAAM,KAAK,SAAS;AAAA,QAC1B,GAAI,MAAM,KAAK,OAAO,EAAE,MAAM,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,MACrD;AAAA,MACA,oBAAoB,MAAM,KAAK,OAAO,cAAc;AAAA,MACpD,iBAAiB;AAAA;AAAA;AAAA;AAAA,QAIf,GAAI,MAAM,mBAAmB,CAAC;AAAA,QAC9B,IAAI,MAAM;AAAA,QACV,IAAI,MAAM,MAAM,CAAC;AAAA,QACjB,KAAK,MAAM,OAAO,CAAC;AAAA,QACnB,SAAS,MAAM;AAAA,QACf,WAAW,MAAM,aAAa;AAAA,QAC9B,YAAY,MAAM,cAAc,CAAC;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,OAAG,QAAQ,WAAW;AACtB,UAAM,GAAG,MAAM;AAAA,EACjB,CAAC;AAED,QAAM,QAAQ,8BAA8B,8BAA8B,QAAQ;AAClF,QAAM,cAAuC;AAAA,IAC3C;AAAA,IACA,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AACA,QAAM,MAAM,QAAQ,WAAiD;AAErE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,IACA,UAAU;AAAA,IACV,WAAW,QAAQ;AAAA,IACnB,aAAa,QAAQ;AAAA,EACvB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const COMMUNICATION_CHANNELS_SYSTEM_USER_ID = "00000000-0000-0000-0000-000000000000";
|
|
2
|
+
function systemUserEmail(tenantId) {
|
|
3
|
+
return `system+communication_channels@${tenantId}.local`;
|
|
4
|
+
}
|
|
5
|
+
async function resolveCommunicationChannelsSystemUserId(em, tenantId, fallbackId) {
|
|
6
|
+
try {
|
|
7
|
+
const expectedEmail = systemUserEmail(tenantId);
|
|
8
|
+
const qb = em.createQueryBuilder("auth.users", "u");
|
|
9
|
+
const row = await qb.select(["u.id"]).where({ email: expectedEmail, tenant_id: tenantId }).limit(1).execute("get").catch(() => null);
|
|
10
|
+
const id = row?.id;
|
|
11
|
+
if (typeof id === "string" && id.length > 0) return id;
|
|
12
|
+
} catch {
|
|
13
|
+
}
|
|
14
|
+
if (typeof fallbackId === "string" && fallbackId.length > 0) return fallbackId;
|
|
15
|
+
return COMMUNICATION_CHANNELS_SYSTEM_USER_ID;
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
COMMUNICATION_CHANNELS_SYSTEM_USER_ID,
|
|
19
|
+
resolveCommunicationChannelsSystemUserId,
|
|
20
|
+
systemUserEmail
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=system-user.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/communication_channels/lib/system-user.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\n\n/**\n * Sentinel UUID \u2014 used as a last-resort `senderUserId` for inbound channel\n * messages when no per-tenant system user is available. Matches the pattern\n * in `inbox_ops/lib/messagesIntegration.ts`.\n */\nexport const COMMUNICATION_CHANNELS_SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000'\n\n/**\n * Configurable email pattern for per-tenant channel-bot users. Implementations\n * (provider packages, onboarding scripts) may create a real `auth.user` row\n * matching this convention so inbound channel messages get a meaningful sender\n * display name in the unified inbox.\n *\n * Format: `system+communication_channels@<tenantId>.local`\n */\nexport function systemUserEmail(tenantId: string): string {\n return `system+communication_channels@${tenantId}.local`\n}\n\n/**\n * Resolve a tenant-scoped system user id to attribute inbound channel messages to.\n *\n * Lookup order:\n * 1. Per-tenant channel-bot user (by convention email \u2014 see `systemUserEmail`).\n * 2. (Optional, caller-supplied) seed fallback \u2014 a specific override id.\n * 3. Sentinel UUID (`00000000-...`) \u2014 backward-compatible default.\n *\n * The function is fail-soft: when the lookup throws, it falls back to the\n * sentinel. The inbound-processor must never refuse to ingest a message\n * because the channel-bot user doesn't exist.\n *\n * @param em EntityManager scoped to the tenant.\n * @param tenantId Tenant id for which to resolve the system user.\n * @param fallbackId Optional caller-supplied fallback (e.g., the channel's\n * assigned user) used when the channel-bot lookup misses.\n */\nexport async function resolveCommunicationChannelsSystemUserId(\n em: EntityManager,\n tenantId: string,\n fallbackId?: string | null,\n): Promise<string> {\n try {\n const expectedEmail = systemUserEmail(tenantId)\n // Untyped QB by design \u2014 the helper is intentionally cross-module\n // (resolving an `auth.user` from the hub) and must not pull the User\n // entity class. MikroORM v7's typed builder requires an entity ref;\n // we keep the lookup table-name-driven so the helper compiles without\n // a cross-module import. The mocks in `__tests__/system-user.test.ts`\n // exercise this code path through a duck-typed `createQueryBuilder` stub.\n type RawQueryBuilder = {\n select: (fields: string[]) => RawQueryBuilder\n where: (cond: Record<string, unknown>) => RawQueryBuilder\n limit: (count: number) => RawQueryBuilder\n execute: (mode: string) => Promise<unknown>\n }\n const qb = (\n em as unknown as { createQueryBuilder: (table: string, alias: string) => RawQueryBuilder }\n ).createQueryBuilder('auth.users', 'u')\n const row = await qb\n .select(['u.id'])\n .where({ email: expectedEmail, tenant_id: tenantId })\n .limit(1)\n .execute('get')\n .catch(() => null)\n const id = (row as { id?: string } | null)?.id\n if (typeof id === 'string' && id.length > 0) return id\n } catch {\n // ignore \u2014 fall through to fallback\n }\n if (typeof fallbackId === 'string' && fallbackId.length > 0) return fallbackId\n return COMMUNICATION_CHANNELS_SYSTEM_USER_ID\n}\n"],
|
|
5
|
+
"mappings": "AAOO,MAAM,wCAAwC;AAU9C,SAAS,gBAAgB,UAA0B;AACxD,SAAO,iCAAiC,QAAQ;AAClD;AAmBA,eAAsB,yCACpB,IACA,UACA,YACiB;AACjB,MAAI;AACF,UAAM,gBAAgB,gBAAgB,QAAQ;AAa9C,UAAM,KACJ,GACA,mBAAmB,cAAc,GAAG;AACtC,UAAM,MAAM,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,EACf,MAAM,EAAE,OAAO,eAAe,WAAW,SAAS,CAAC,EACnD,MAAM,CAAC,EACP,QAAQ,KAAK,EACb,MAAM,MAAM,IAAI;AACnB,UAAM,KAAM,KAAgC;AAC5C,QAAI,OAAO,OAAO,YAAY,GAAG,SAAS,EAAG,QAAO;AAAA,EACtD,QAAQ;AAAA,EAER;AACA,MAAI,OAAO,eAAe,YAAY,WAAW,SAAS,EAAG,QAAO;AACpE,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { baseEmailCapabilities } from "./email-capabilities.js";
|
|
2
|
+
import { hasChannelAdapter, registerChannelAdapter } from "./adapter-registry-singleton.js";
|
|
3
|
+
const TEST_SEED_PROVIDER_KEY = "__test_seed__";
|
|
4
|
+
const TEST_CHANNEL_SEEDING_ENV = "OM_ENABLE_TEST_CHANNEL_SEEDING";
|
|
5
|
+
function isTestChannelSeedingEnabled() {
|
|
6
|
+
const raw = process.env[TEST_CHANNEL_SEEDING_ENV];
|
|
7
|
+
if (typeof raw !== "string") return false;
|
|
8
|
+
return ["1", "true", "yes", "on"].includes(raw.trim().toLowerCase());
|
|
9
|
+
}
|
|
10
|
+
const testSeedCapabilities = {
|
|
11
|
+
...baseEmailCapabilities,
|
|
12
|
+
conversationHistory: false,
|
|
13
|
+
realtimePush: false
|
|
14
|
+
};
|
|
15
|
+
class TestSeedChannelAdapter {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.providerKey = TEST_SEED_PROVIDER_KEY;
|
|
18
|
+
this.channelType = "email";
|
|
19
|
+
this.capabilities = testSeedCapabilities;
|
|
20
|
+
}
|
|
21
|
+
async sendMessage(input) {
|
|
22
|
+
const externalMessageId = `test-seed-${Date.now()}-${Math.random().toString(16).slice(2, 10)}@test-seed.local`;
|
|
23
|
+
return {
|
|
24
|
+
externalMessageId,
|
|
25
|
+
conversationId: input.conversationId,
|
|
26
|
+
status: "sent",
|
|
27
|
+
metadata: { testSeed: true }
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async verifyWebhook(_input) {
|
|
31
|
+
return { raw: {}, eventType: "other", metadata: { reason: "test-seed-no-webhook" } };
|
|
32
|
+
}
|
|
33
|
+
async getStatus(_input) {
|
|
34
|
+
return { status: "sent" };
|
|
35
|
+
}
|
|
36
|
+
async convertOutbound(input) {
|
|
37
|
+
return {
|
|
38
|
+
content: {
|
|
39
|
+
text: input.body,
|
|
40
|
+
bodyFormat: input.bodyFormat
|
|
41
|
+
},
|
|
42
|
+
metadata: input.channelMetadata ?? {}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async normalizeInbound(_raw) {
|
|
46
|
+
throw new Error("[internal] TestSeedChannelAdapter.normalizeInbound is not used by the seed harness");
|
|
47
|
+
}
|
|
48
|
+
async validateCredentials(_input) {
|
|
49
|
+
return { ok: true };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
let cachedTestSeedAdapter = null;
|
|
53
|
+
function getTestSeedChannelAdapter() {
|
|
54
|
+
if (!cachedTestSeedAdapter) cachedTestSeedAdapter = new TestSeedChannelAdapter();
|
|
55
|
+
return cachedTestSeedAdapter;
|
|
56
|
+
}
|
|
57
|
+
function ensureTestSeedAdapterRegistered() {
|
|
58
|
+
if (!isTestChannelSeedingEnabled()) return;
|
|
59
|
+
if (hasChannelAdapter(TEST_SEED_PROVIDER_KEY)) return;
|
|
60
|
+
registerChannelAdapter(getTestSeedChannelAdapter());
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
TEST_CHANNEL_SEEDING_ENV,
|
|
64
|
+
TEST_SEED_PROVIDER_KEY,
|
|
65
|
+
ensureTestSeedAdapterRegistered,
|
|
66
|
+
isTestChannelSeedingEnabled
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=test-seed.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/communication_channels/lib/test-seed.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n ChannelAdapter,\n ChannelCapabilities,\n ChannelNativeContent,\n ConvertOutboundInput,\n GetMessageStatusInput,\n InboundMessage,\n MessageStatus,\n NormalizedInboundMessage,\n SendMessageInput,\n SendMessageResult,\n ValidateCredentialsInput,\n ValidateCredentialsResult,\n VerifyWebhookInput,\n} from './adapter'\nimport { baseEmailCapabilities } from './email-capabilities'\nimport { hasChannelAdapter, registerChannelAdapter } from './adapter-registry-singleton'\n\n/**\n * Test-only channel seeding support.\n *\n * The ephemeral integration harness cannot connect a REAL email channel:\n * - IMAP/SMTP `validateCredentials` performs a live LOGIN against a mail server\n * (none exists in CI), so `POST /channels/connect/credentials` returns 422.\n * - Even with a connected channel, the outbound delivery worker calls the real\n * SMTP adapter, which fails with no server \u2014 so `communication_channels.message.sent`\n * never fires and the customers link subscriber never runs.\n *\n * To make the compose \u2192 deliver \u2192 `.sent` \u2192 CRM-link \u2192 cross-user-visibility chain\n * (TC-CRM-EMAIL-001) and the inbound auto-link chain (TC-CRM-EMAIL-002..005) runnable\n * end-to-end against real Postgres, this module provides a network-free stub adapter\n * that is registered ONLY when `OM_ENABLE_TEST_CHANNEL_SEEDING` is set.\n *\n * Production safety: the registration is gated by {@link isTestChannelSeedingEnabled};\n * when the env flag is unset (the production default) the adapter is never registered\n * and the `__test_seed__` provider key resolves to no adapter \u2014 so the connect route\n * returns 404 `no_adapter` exactly as it would for any unknown provider. The dedicated\n * test-seed API route enforces the same gate independently (fail-closed 404 in prod).\n */\n\n/** Provider key for the network-free test stub adapter. */\nexport const TEST_SEED_PROVIDER_KEY = '__test_seed__'\n\n/** Env flag that unlocks test-only channel seeding. Off in production. */\nexport const TEST_CHANNEL_SEEDING_ENV = 'OM_ENABLE_TEST_CHANNEL_SEEDING'\n\n/**\n * True only when the test-seeding env flag is explicitly enabled. Accepts the\n * usual truthy tokens (`1`, `true`, `yes`, `on`) so the harness can opt in via a\n * plain `=true`. Any other value (including unset) is treated as disabled.\n */\nexport function isTestChannelSeedingEnabled(): boolean {\n const raw = process.env[TEST_CHANNEL_SEEDING_ENV]\n if (typeof raw !== 'string') return false\n return ['1', 'true', 'yes', 'on'].includes(raw.trim().toLowerCase())\n}\n\n/**\n * Capabilities for the stub: an email channel that supports neither reactions,\n * edit/delete, nor conversation history \u2014 so the strict registry validator\n * (`validateAdapterCapabilities`) requires only the core method surface.\n */\nconst testSeedCapabilities: ChannelCapabilities = {\n ...baseEmailCapabilities,\n conversationHistory: false,\n realtimePush: false,\n}\n\n/**\n * A `ChannelAdapter` whose `sendMessage` reports a successful send WITHOUT any\n * network I/O. Used exclusively by the integration harness to let the outbound\n * delivery worker reach its success path and emit `communication_channels.message.sent`.\n */\nclass TestSeedChannelAdapter implements ChannelAdapter {\n readonly providerKey = TEST_SEED_PROVIDER_KEY\n readonly channelType = 'email'\n readonly capabilities = testSeedCapabilities\n\n async sendMessage(input: SendMessageInput): Promise<SendMessageResult> {\n // Synthesize a deterministic-looking RFC2822-style message id; never touches\n // the network. The delivery worker persists this as the external message id.\n const externalMessageId = `test-seed-${Date.now()}-${Math.random().toString(16).slice(2, 10)}@test-seed.local`\n return {\n externalMessageId,\n conversationId: input.conversationId,\n status: 'sent',\n metadata: { testSeed: true },\n }\n }\n\n async verifyWebhook(_input: VerifyWebhookInput): Promise<InboundMessage> {\n // No real webhook \u2014 return the inert event so the generic webhook route 202s\n // without enqueuing tenant-scoped work (mirrors the IMAP adapter contract).\n return { raw: {}, eventType: 'other', metadata: { reason: 'test-seed-no-webhook' } }\n }\n\n async getStatus(_input: GetMessageStatusInput): Promise<MessageStatus> {\n return { status: 'sent' }\n }\n\n async convertOutbound(input: ConvertOutboundInput): Promise<ChannelNativeContent> {\n return {\n content: {\n text: input.body,\n bodyFormat: input.bodyFormat,\n },\n metadata: input.channelMetadata ?? {},\n }\n }\n\n async normalizeInbound(_raw: InboundMessage): Promise<NormalizedInboundMessage> {\n // The test-seed inbound path seeds MessageChannelLink rows directly and emits\n // the hub event, so this adapter never normalizes a raw inbound payload.\n throw new Error('[internal] TestSeedChannelAdapter.normalizeInbound is not used by the seed harness')\n }\n\n async validateCredentials(_input: ValidateCredentialsInput): Promise<ValidateCredentialsResult> {\n // No real server to authenticate against \u2014 accept any credentials so the\n // connect command persists a connected channel.\n return { ok: true }\n }\n}\n\nlet cachedTestSeedAdapter: TestSeedChannelAdapter | null = null\n\nfunction getTestSeedChannelAdapter(): TestSeedChannelAdapter {\n if (!cachedTestSeedAdapter) cachedTestSeedAdapter = new TestSeedChannelAdapter()\n return cachedTestSeedAdapter\n}\n\n/**\n * Register the test-seed adapter exactly once, but ONLY when the env flag is set.\n * Idempotent and safe to call from every container creation (`di.register`) \u2014 a\n * no-op when seeding is disabled or the adapter is already registered.\n */\nexport function ensureTestSeedAdapterRegistered(): void {\n if (!isTestChannelSeedingEnabled()) return\n if (hasChannelAdapter(TEST_SEED_PROVIDER_KEY)) return\n registerChannelAdapter(getTestSeedChannelAdapter())\n}\n"],
|
|
5
|
+
"mappings": "AAeA,SAAS,6BAA6B;AACtC,SAAS,mBAAmB,8BAA8B;AAyBnD,MAAM,yBAAyB;AAG/B,MAAM,2BAA2B;AAOjC,SAAS,8BAAuC;AACrD,QAAM,MAAM,QAAQ,IAAI,wBAAwB;AAChD,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,SAAO,CAAC,KAAK,QAAQ,OAAO,IAAI,EAAE,SAAS,IAAI,KAAK,EAAE,YAAY,CAAC;AACrE;AAOA,MAAM,uBAA4C;AAAA,EAChD,GAAG;AAAA,EACH,qBAAqB;AAAA,EACrB,cAAc;AAChB;AAOA,MAAM,uBAAiD;AAAA,EAAvD;AACE,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,eAAe;AAAA;AAAA,EAExB,MAAM,YAAY,OAAqD;AAGrE,UAAM,oBAAoB,aAAa,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AAC5F,WAAO;AAAA,MACL;AAAA,MACA,gBAAgB,MAAM;AAAA,MACtB,QAAQ;AAAA,MACR,UAAU,EAAE,UAAU,KAAK;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,QAAqD;AAGvE,WAAO,EAAE,KAAK,CAAC,GAAG,WAAW,SAAS,UAAU,EAAE,QAAQ,uBAAuB,EAAE;AAAA,EACrF;AAAA,EAEA,MAAM,UAAU,QAAuD;AACrE,WAAO,EAAE,QAAQ,OAAO;AAAA,EAC1B;AAAA,EAEA,MAAM,gBAAgB,OAA4D;AAChF,WAAO;AAAA,MACL,SAAS;AAAA,QACP,MAAM,MAAM;AAAA,QACZ,YAAY,MAAM;AAAA,MACpB;AAAA,MACA,UAAU,MAAM,mBAAmB,CAAC;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,MAAM,iBAAiB,MAAyD;AAG9E,UAAM,IAAI,MAAM,oFAAoF;AAAA,EACtG;AAAA,EAEA,MAAM,oBAAoB,QAAsE;AAG9F,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AACF;AAEA,IAAI,wBAAuD;AAE3D,SAAS,4BAAoD;AAC3D,MAAI,CAAC,sBAAuB,yBAAwB,IAAI,uBAAuB;AAC/E,SAAO;AACT;AAOO,SAAS,kCAAwC;AACtD,MAAI,CAAC,4BAA4B,EAAG;AACpC,MAAI,kBAAkB,sBAAsB,EAAG;AAC/C,yBAAuB,0BAA0B,CAAC;AACpD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|