@open-mercato/core 0.6.7-develop.6788.1.c2a1520a30 → 0.6.7-develop.6814.1.0627c7e9f1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/customer_deal_person_link/index.js +2 -0
- package/dist/generated/entities/customer_deal_person_link/index.js.map +2 -2
- package/dist/generated/entities/customer_user_invitation/index.js +2 -0
- package/dist/generated/entities/customer_user_invitation/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/business_rules/api/openmercato-call-options/route.js +85 -0
- package/dist/modules/business_rules/api/openmercato-call-options/route.js.map +7 -0
- package/dist/modules/business_rules/api/rules/route.js +71 -4
- package/dist/modules/business_rules/api/rules/route.js.map +2 -2
- package/dist/modules/business_rules/components/ActionBuilder.js +47 -4
- package/dist/modules/business_rules/components/ActionBuilder.js.map +3 -3
- package/dist/modules/business_rules/components/ActionRow.js +106 -16
- package/dist/modules/business_rules/components/ActionRow.js.map +2 -2
- package/dist/modules/business_rules/components/utils/actionValidation.js +6 -0
- package/dist/modules/business_rules/components/utils/actionValidation.js.map +2 -2
- package/dist/modules/business_rules/lib/action-executor.js +148 -0
- package/dist/modules/business_rules/lib/action-executor.js.map +2 -2
- package/dist/modules/business_rules/lib/openmercato-call-options-types.js +5 -0
- package/dist/modules/business_rules/lib/openmercato-call-options-types.js.map +7 -0
- package/dist/modules/business_rules/lib/openmercato-call-options.js +231 -0
- package/dist/modules/business_rules/lib/openmercato-call-options.js.map +7 -0
- package/dist/modules/business_rules/lib/rule-engine.js +5 -1
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +11 -1
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +11 -6
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +12 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users/[id].js +10 -1
- package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users-invite.js +61 -4
- package/dist/modules/customer_accounts/api/admin/users-invite.js.map +2 -2
- package/dist/modules/customer_accounts/api/portal/users-invite.js +20 -2
- package/dist/modules/customer_accounts/api/portal/users-invite.js.map +2 -2
- package/dist/modules/customer_accounts/data/entities.js +3 -0
- package/dist/modules/customer_accounts/data/entities.js.map +2 -2
- package/dist/modules/customer_accounts/data/validators.js +1 -0
- package/dist/modules/customer_accounts/data/validators.js.map +2 -2
- package/dist/modules/customer_accounts/emails/CustomerInvitationEmail.js +26 -0
- package/dist/modules/customer_accounts/emails/CustomerInvitationEmail.js.map +7 -0
- package/dist/modules/customer_accounts/lib/customerEntityOwnership.js +29 -3
- package/dist/modules/customer_accounts/lib/customerEntityOwnership.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerUrl.js +19 -10
- package/dist/modules/customer_accounts/lib/customerUrl.js.map +2 -2
- package/dist/modules/customer_accounts/lib/invitationEmail.js +35 -0
- package/dist/modules/customer_accounts/lib/invitationEmail.js.map +7 -0
- package/dist/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.js +13 -0
- package/dist/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.js.map +7 -0
- package/dist/modules/customer_accounts/services/customerInvitationService.js +32 -2
- package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
- package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js +47 -7
- package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection-table.js +1 -1
- package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +4 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +7 -3
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +11 -1
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +14 -8
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +21 -6
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/cli.js +17 -7
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +48 -10
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/people.js +2 -0
- package/dist/modules/customers/commands/people.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyDetailTabs.js +26 -6
- package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/DealDetailTabs.js +30 -8
- package/dist/modules/customers/components/detail/DealDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +30 -8
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/data/entities.js +8 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +2 -1
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260723210000_customers.js +15 -0
- package/dist/modules/customers/migrations/Migration20260723210000_customers.js.map +7 -0
- package/dist/modules/customers/search.js +50 -29
- package/dist/modules/customers/search.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/[id]/route.js +22 -9
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/route.js +13 -4
- package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
- package/dist/modules/data_sync/api/options.js +7 -2
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +7 -2
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +8 -3
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +7 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/route.js +8 -3
- package/dist/modules/data_sync/api/runs/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/runs.js +7 -2
- package/dist/modules/data_sync/api/runs.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +19 -6
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +13 -4
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/data_sync/api/validate.js +9 -4
- package/dist/modules/data_sync/api/validate.js.map +2 -2
- package/dist/modules/design_system/acl.js +9 -0
- package/dist/modules/design_system/acl.js.map +7 -0
- package/dist/modules/design_system/backend/design-system/page.js +11 -0
- package/dist/modules/design_system/backend/design-system/page.js.map +7 -0
- package/dist/modules/design_system/backend/design-system/page.meta.js +15 -0
- package/dist/modules/design_system/backend/design-system/page.meta.js.map +7 -0
- package/dist/modules/design_system/gallery/components/CodeSnippet.js +52 -0
- package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +7 -0
- package/dist/modules/design_system/gallery/components/EntryCard.js +63 -0
- package/dist/modules/design_system/gallery/components/EntryCard.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryShell.js +163 -0
- package/dist/modules/design_system/gallery/components/GalleryShell.js.map +7 -0
- package/dist/modules/design_system/gallery/components/VariantPreview.js +9 -0
- package/dist/modules/design_system/gallery/components/VariantPreview.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/banners.js +186 -0
- package/dist/modules/design_system/gallery/entries/banners.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/buttons.js +361 -0
- package/dist/modules/design_system/gallery/entries/buttons.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/charts.js +457 -0
- package/dist/modules/design_system/gallery/entries/charts.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/dates.js +306 -0
- package/dist/modules/design_system/gallery/entries/dates.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/detail.js +358 -0
- package/dist/modules/design_system/gallery/entries/detail.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/display.js +785 -0
- package/dist/modules/design_system/gallery/entries/display.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/feedback.js +658 -0
- package/dist/modules/design_system/gallery/entries/feedback.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/filters.js +505 -0
- package/dist/modules/design_system/gallery/entries/filters.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/foundations.js +453 -0
- package/dist/modules/design_system/gallery/entries/foundations.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/icons.js +140 -0
- package/dist/modules/design_system/gallery/entries/icons.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/inputs.js +1217 -0
- package/dist/modules/design_system/gallery/entries/inputs.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/messages.js +334 -0
- package/dist/modules/design_system/gallery/entries/messages.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/navigation.js +475 -0
- package/dist/modules/design_system/gallery/entries/navigation.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/notifications.js +257 -0
- package/dist/modules/design_system/gallery/entries/notifications.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/overlays.js +576 -0
- package/dist/modules/design_system/gallery/entries/overlays.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/scaffolding.js +378 -0
- package/dist/modules/design_system/gallery/entries/scaffolding.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/schedule.js +297 -0
- package/dist/modules/design_system/gallery/entries/schedule.js.map +7 -0
- package/dist/modules/design_system/gallery/registry.js +139 -0
- package/dist/modules/design_system/gallery/registry.js.map +7 -0
- package/dist/modules/design_system/gallery/types.js +1 -0
- package/dist/modules/design_system/gallery/types.js.map +7 -0
- package/dist/modules/design_system/index.js +9 -0
- package/dist/modules/design_system/index.js.map +7 -0
- package/dist/modules/design_system/setup.js +14 -0
- package/dist/modules/design_system/setup.js.map +7 -0
- package/dist/modules/entities/api/entity-settings.js +59 -9
- package/dist/modules/entities/api/entity-settings.js.map +2 -2
- package/dist/modules/entities/backend/config/settings/page.js +10 -0
- package/dist/modules/entities/backend/config/settings/page.js.map +7 -0
- package/dist/modules/entities/backend/config/settings/page.meta.js +36 -0
- package/dist/modules/entities/backend/config/settings/page.meta.js.map +7 -0
- package/dist/modules/entities/backend/entities/user/create/page.js +16 -7
- package/dist/modules/entities/backend/entities/user/create/page.js.map +2 -2
- package/dist/modules/entities/components/EntitySettingsManager.js +136 -0
- package/dist/modules/entities/components/EntitySettingsManager.js.map +7 -0
- package/dist/modules/inbox_ops/api/webhook/inbound.js +14 -10
- package/dist/modules/inbox_ops/api/webhook/inbound.js.map +2 -2
- package/dist/modules/inbox_ops/search.js +15 -4
- package/dist/modules/inbox_ops/search.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +11 -5
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/health/route.js +6 -3
- package/dist/modules/integrations/api/[id]/health/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +6 -3
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/state/route.js +6 -3
- package/dist/modules/integrations/api/[id]/state/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/version/route.js +6 -3
- package/dist/modules/integrations/api/[id]/version/route.js.map +2 -2
- package/dist/modules/integrations/api/logs/route.js +6 -3
- package/dist/modules/integrations/api/logs/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +6 -3
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/api/umes-read.js +2 -2
- package/dist/modules/integrations/api/umes-read.js.map +2 -2
- package/dist/modules/integrations/lib/organization-scope.js +2 -9
- package/dist/modules/integrations/lib/organization-scope.js.map +2 -2
- package/dist/modules/messages/search.js +10 -5
- package/dist/modules/messages/search.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +11 -1
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/data/entities.js +4 -0
- package/dist/modules/payment_gateways/data/entities.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +12 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/payment_gateways/lib/session-idempotency.js +33 -0
- package/dist/modules/payment_gateways/lib/session-idempotency.js.map +2 -2
- package/dist/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.js +18 -0
- package/dist/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.js.map +7 -0
- package/dist/modules/payment_gateways/setup.js +47 -0
- package/dist/modules/payment_gateways/setup.js.map +2 -2
- package/dist/modules/payment_gateways/workers/session-initialization-prune.js +50 -0
- package/dist/modules/payment_gateways/workers/session-initialization-prune.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js +134 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.js +11 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.js.map +7 -0
- package/dist/modules/portal/lib/navigation.js +7 -0
- package/dist/modules/portal/lib/navigation.js.map +7 -0
- package/dist/modules/query_index/data/entities.js +2 -1
- package/dist/modules/query_index/data/entities.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +75 -50
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/search-tokens.js +15 -5
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js +17 -0
- package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js.map +7 -0
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +11 -1
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +2 -2
- package/generated/entities/customer_deal_person_link/index.ts +1 -0
- package/generated/entities/customer_user_invitation/index.ts +1 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +16 -16
- package/src/modules/business_rules/api/openmercato-call-options/route.ts +94 -0
- package/src/modules/business_rules/api/rules/route.ts +92 -4
- package/src/modules/business_rules/components/ActionBuilder.tsx +54 -5
- package/src/modules/business_rules/components/ActionRow.tsx +156 -18
- package/src/modules/business_rules/components/utils/actionValidation.ts +6 -0
- package/src/modules/business_rules/i18n/de.json +12 -0
- package/src/modules/business_rules/i18n/en.json +12 -0
- package/src/modules/business_rules/i18n/es.json +12 -0
- package/src/modules/business_rules/i18n/pl.json +12 -0
- package/src/modules/business_rules/lib/action-executor.ts +198 -0
- package/src/modules/business_rules/lib/openmercato-call-options-types.ts +31 -0
- package/src/modules/business_rules/lib/openmercato-call-options.ts +297 -0
- package/src/modules/business_rules/lib/rule-engine.ts +4 -0
- package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +11 -1
- package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +17 -11
- package/src/modules/configs/lib/upgrade-actions.ts +14 -0
- package/src/modules/customer_accounts/api/admin/users/[id].ts +16 -1
- package/src/modules/customer_accounts/api/admin/users-invite.ts +78 -3
- package/src/modules/customer_accounts/api/portal/users-invite.ts +22 -1
- package/src/modules/customer_accounts/data/entities.ts +3 -0
- package/src/modules/customer_accounts/data/validators.ts +1 -0
- package/src/modules/customer_accounts/emails/CustomerInvitationEmail.tsx +45 -0
- package/src/modules/customer_accounts/i18n/de.json +6 -0
- package/src/modules/customer_accounts/i18n/en.json +6 -0
- package/src/modules/customer_accounts/i18n/es.json +6 -0
- package/src/modules/customer_accounts/i18n/pl.json +6 -0
- package/src/modules/customer_accounts/lib/customerEntityOwnership.ts +68 -2
- package/src/modules/customer_accounts/lib/customerUrl.ts +27 -11
- package/src/modules/customer_accounts/lib/invitationEmail.ts +42 -0
- package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +16 -0
- package/src/modules/customer_accounts/migrations/Migration20260723120000_customer_accounts.ts +13 -0
- package/src/modules/customer_accounts/services/customerInvitationService.ts +56 -3
- package/src/modules/customer_accounts/subscribers/autoLinkCrm.ts +75 -8
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +2 -2
- package/src/modules/customer_accounts/widgets/injection-table.ts +4 -1
- package/src/modules/customers/api/deals/[id]/people/route.ts +3 -0
- package/src/modules/customers/api/deals/[id]/route.ts +10 -2
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +12 -1
- package/src/modules/customers/backend/customers/deals/[id]/hooks/types.ts +1 -0
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +15 -9
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +24 -6
- package/src/modules/customers/cli.ts +17 -7
- package/src/modules/customers/commands/deals.ts +50 -9
- package/src/modules/customers/commands/people.ts +3 -0
- package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +30 -7
- package/src/modules/customers/components/detail/DealDetailTabs.tsx +34 -9
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +34 -9
- package/src/modules/customers/data/entities.ts +9 -1
- package/src/modules/customers/data/validators.ts +1 -0
- package/src/modules/customers/i18n/de.json +18 -0
- package/src/modules/customers/i18n/en.json +18 -0
- package/src/modules/customers/i18n/es.json +18 -0
- package/src/modules/customers/i18n/pl.json +18 -0
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +27 -1
- package/src/modules/customers/migrations/Migration20260723210000_customers.ts +15 -0
- package/src/modules/customers/search.ts +59 -27
- package/src/modules/data_sync/AGENTS.md +1 -0
- package/src/modules/data_sync/api/mappings/[id]/route.ts +22 -9
- package/src/modules/data_sync/api/mappings/route.ts +13 -4
- package/src/modules/data_sync/api/options.ts +7 -2
- package/src/modules/data_sync/api/run.ts +7 -2
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +8 -3
- package/src/modules/data_sync/api/runs/[id]/retry.ts +7 -2
- package/src/modules/data_sync/api/runs/[id]/route.ts +8 -3
- package/src/modules/data_sync/api/runs.ts +7 -2
- package/src/modules/data_sync/api/schedules/[id]/route.ts +19 -6
- package/src/modules/data_sync/api/schedules/route.ts +13 -4
- package/src/modules/data_sync/api/validate.ts +9 -4
- package/src/modules/design_system/AGENTS.md +34 -0
- package/src/modules/design_system/acl.ts +5 -0
- package/src/modules/design_system/backend/design-system/page.meta.ts +11 -0
- package/src/modules/design_system/backend/design-system/page.tsx +14 -0
- package/src/modules/design_system/gallery/components/CodeSnippet.tsx +55 -0
- package/src/modules/design_system/gallery/components/EntryCard.tsx +95 -0
- package/src/modules/design_system/gallery/components/GalleryShell.tsx +206 -0
- package/src/modules/design_system/gallery/components/VariantPreview.tsx +15 -0
- package/src/modules/design_system/gallery/entries/banners.tsx +226 -0
- package/src/modules/design_system/gallery/entries/buttons.tsx +422 -0
- package/src/modules/design_system/gallery/entries/charts.tsx +518 -0
- package/src/modules/design_system/gallery/entries/dates.tsx +325 -0
- package/src/modules/design_system/gallery/entries/detail.tsx +403 -0
- package/src/modules/design_system/gallery/entries/display.tsx +893 -0
- package/src/modules/design_system/gallery/entries/feedback.tsx +746 -0
- package/src/modules/design_system/gallery/entries/filters.tsx +570 -0
- package/src/modules/design_system/gallery/entries/foundations.tsx +529 -0
- package/src/modules/design_system/gallery/entries/icons.tsx +168 -0
- package/src/modules/design_system/gallery/entries/inputs.tsx +1516 -0
- package/src/modules/design_system/gallery/entries/messages.tsx +339 -0
- package/src/modules/design_system/gallery/entries/navigation.tsx +573 -0
- package/src/modules/design_system/gallery/entries/notifications.tsx +285 -0
- package/src/modules/design_system/gallery/entries/overlays.tsx +677 -0
- package/src/modules/design_system/gallery/entries/scaffolding.tsx +430 -0
- package/src/modules/design_system/gallery/entries/schedule.tsx +307 -0
- package/src/modules/design_system/gallery/registry.ts +152 -0
- package/src/modules/design_system/gallery/types.ts +32 -0
- package/src/modules/design_system/i18n/.hardcoded-allowlist.json +71 -0
- package/src/modules/design_system/i18n/de.json +46 -0
- package/src/modules/design_system/i18n/en.json +46 -0
- package/src/modules/design_system/i18n/es.json +46 -0
- package/src/modules/design_system/i18n/pl.json +46 -0
- package/src/modules/design_system/index.ts +7 -0
- package/src/modules/design_system/setup.ts +12 -0
- package/src/modules/entities/api/entity-settings.ts +66 -22
- package/src/modules/entities/backend/config/settings/page.meta.ts +34 -0
- package/src/modules/entities/backend/config/settings/page.tsx +12 -0
- package/src/modules/entities/backend/entities/user/create/page.tsx +16 -7
- package/src/modules/entities/components/EntitySettingsManager.tsx +160 -0
- package/src/modules/entities/i18n/de.json +9 -0
- package/src/modules/entities/i18n/en.json +9 -0
- package/src/modules/entities/i18n/es.json +9 -0
- package/src/modules/entities/i18n/pl.json +9 -0
- package/src/modules/inbox_ops/api/webhook/inbound.ts +14 -12
- package/src/modules/inbox_ops/i18n/de.json +3 -0
- package/src/modules/inbox_ops/i18n/en.json +3 -0
- package/src/modules/inbox_ops/i18n/es.json +3 -0
- package/src/modules/inbox_ops/i18n/pl.json +3 -0
- package/src/modules/inbox_ops/search.ts +20 -4
- package/src/modules/integrations/api/[id]/credentials/route.ts +11 -5
- package/src/modules/integrations/api/[id]/health/route.ts +6 -3
- package/src/modules/integrations/api/[id]/route.ts +6 -3
- package/src/modules/integrations/api/[id]/state/route.ts +6 -3
- package/src/modules/integrations/api/[id]/version/route.ts +6 -3
- package/src/modules/integrations/api/logs/route.ts +6 -3
- package/src/modules/integrations/api/route.ts +6 -3
- package/src/modules/integrations/api/umes-read.ts +2 -2
- package/src/modules/integrations/lib/organization-scope.ts +5 -25
- package/src/modules/messages/i18n/de.json +2 -0
- package/src/modules/messages/i18n/en.json +2 -0
- package/src/modules/messages/i18n/es.json +2 -0
- package/src/modules/messages/i18n/pl.json +2 -0
- package/src/modules/messages/search.ts +11 -5
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +13 -1
- package/src/modules/payment_gateways/data/entities.ts +5 -0
- package/src/modules/payment_gateways/i18n/de.json +5 -1
- package/src/modules/payment_gateways/i18n/en.json +5 -1
- package/src/modules/payment_gateways/i18n/es.json +5 -1
- package/src/modules/payment_gateways/i18n/pl.json +5 -1
- package/src/modules/payment_gateways/lib/gateway-service.ts +12 -2
- package/src/modules/payment_gateways/lib/session-idempotency.ts +36 -0
- package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +10 -1
- package/src/modules/payment_gateways/migrations/Migration20260803103035_payment_gateways.ts +17 -0
- package/src/modules/payment_gateways/setup.ts +62 -0
- package/src/modules/payment_gateways/workers/session-initialization-prune.ts +63 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.meta.ts +9 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.tsx +164 -0
- package/src/modules/portal/i18n/de.json +18 -0
- package/src/modules/portal/i18n/en.json +18 -0
- package/src/modules/portal/i18n/es.json +18 -0
- package/src/modules/portal/i18n/pl.json +18 -0
- package/src/modules/portal/lib/navigation.ts +3 -0
- package/src/modules/query_index/data/entities.ts +7 -0
- package/src/modules/query_index/lib/engine.ts +92 -63
- package/src/modules/query_index/lib/search-tokens.ts +16 -4
- package/src/modules/query_index/migrations/.snapshot-open-mercato.json +12 -0
- package/src/modules/query_index/migrations/Migration20260731105052_query_index.ts +35 -0
- package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +11 -1
|
@@ -20,16 +20,60 @@ import {
|
|
|
20
20
|
type ResolvedJoin,
|
|
21
21
|
} from '@open-mercato/shared/lib/query/join-utils'
|
|
22
22
|
import { resolveSearchConfig, type SearchConfig } from '@open-mercato/shared/lib/search/config'
|
|
23
|
+
import {
|
|
24
|
+
createSearchTokenAvailability,
|
|
25
|
+
isSearchFilterOp,
|
|
26
|
+
hasSearchFilter,
|
|
27
|
+
type SearchTokenAvailability,
|
|
28
|
+
type SearchTokenProbeDb,
|
|
29
|
+
type SearchTokenProbeQueryBuilder,
|
|
30
|
+
} from '@open-mercato/shared/lib/search/availability'
|
|
23
31
|
import { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'
|
|
24
32
|
import { runBeforeQueryPipeline, runAfterQueryPipeline, type QueryExtensionContext } from '@open-mercato/shared/lib/query/query-extension-runner'
|
|
25
33
|
import { warnOnCiphertextLikeFallback } from '@open-mercato/shared/lib/query/ciphertext-search-warning'
|
|
26
34
|
import { resolveEncryptedSortFields, resolveEncryptedSortMaxRows, sortRowsInMemory } from '@open-mercato/shared/lib/query/encrypted-sort'
|
|
27
35
|
import { mapWithConcurrency } from '@open-mercato/shared/lib/query/bounded-decrypt'
|
|
28
36
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
37
|
+
import { parseNumberWithDefault } from '@open-mercato/shared/lib/number'
|
|
29
38
|
|
|
30
39
|
const logger = createLogger('query_index').child({ component: 'engine' })
|
|
31
40
|
|
|
32
41
|
const DECRYPT_CONCURRENCY = 8
|
|
42
|
+
const AUTO_REINDEX_DEBOUNCE_DEFAULT_MS = 30_000
|
|
43
|
+
const AUTO_REINDEX_DEBOUNCE_MAX_SCOPES = 10_000
|
|
44
|
+
const AUTO_REINDEX_SCHEDULED_AT_KEY = Symbol.for('@open-mercato/query-index/auto-reindex-scheduled-at')
|
|
45
|
+
|
|
46
|
+
type GlobalWithAutoReindexSchedule = typeof globalThis & {
|
|
47
|
+
[AUTO_REINDEX_SCHEDULED_AT_KEY]?: Map<string, number>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getAutoReindexScheduledAt(): Map<string, number> {
|
|
51
|
+
const globalScope = globalThis as GlobalWithAutoReindexSchedule
|
|
52
|
+
if (!globalScope[AUTO_REINDEX_SCHEDULED_AT_KEY]) {
|
|
53
|
+
globalScope[AUTO_REINDEX_SCHEDULED_AT_KEY] = new Map<string, number>()
|
|
54
|
+
}
|
|
55
|
+
return globalScope[AUTO_REINDEX_SCHEDULED_AT_KEY]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function markAutoReindexScheduled(key: string, debounceMs: number, now: number): boolean {
|
|
59
|
+
const autoReindexScheduledAt = getAutoReindexScheduledAt()
|
|
60
|
+
const previous = autoReindexScheduledAt.get(key)
|
|
61
|
+
if (previous !== undefined && now - previous < debounceMs) return false
|
|
62
|
+
|
|
63
|
+
if (autoReindexScheduledAt.size >= AUTO_REINDEX_DEBOUNCE_MAX_SCOPES) {
|
|
64
|
+
for (const [scopeKey, scheduledAt] of autoReindexScheduledAt) {
|
|
65
|
+
if (now - scheduledAt >= debounceMs) autoReindexScheduledAt.delete(scopeKey)
|
|
66
|
+
}
|
|
67
|
+
if (autoReindexScheduledAt.size >= AUTO_REINDEX_DEBOUNCE_MAX_SCOPES) {
|
|
68
|
+
const oldestKey = autoReindexScheduledAt.keys().next().value
|
|
69
|
+
if (oldestKey !== undefined) autoReindexScheduledAt.delete(oldestKey)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
autoReindexScheduledAt.delete(key)
|
|
74
|
+
autoReindexScheduledAt.set(key, now)
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
33
77
|
|
|
34
78
|
function buildFilterableCustomFieldJoins(
|
|
35
79
|
sources: QueryCustomFieldSource[] | undefined,
|
|
@@ -144,8 +188,10 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
144
188
|
private sqlDebugEnabled: boolean | null = null
|
|
145
189
|
private forcePartialIndexEnabled: boolean | null = null
|
|
146
190
|
private autoReindexEnabled: boolean | null = null
|
|
191
|
+
private autoReindexDebounceMs: number | null = null
|
|
147
192
|
private coverageOptimizationEnabled: boolean | null = null
|
|
148
193
|
private pendingCoverageRefreshKeys = new Set<string>()
|
|
194
|
+
private searchAvailabilityInstance: SearchTokenAvailability | null = null
|
|
149
195
|
|
|
150
196
|
constructor(
|
|
151
197
|
private em: EntityManager,
|
|
@@ -174,6 +220,22 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
174
220
|
throw new Error('HybridQueryEngine requires an EntityManager exposing getKysely() (MikroORM v7)')
|
|
175
221
|
}
|
|
176
222
|
|
|
223
|
+
private searchAvailability(): SearchTokenAvailability {
|
|
224
|
+
if (!this.searchAvailabilityInstance) {
|
|
225
|
+
this.searchAvailabilityInstance = createSearchTokenAvailability({
|
|
226
|
+
getDb: () => this.getDb() as unknown as SearchTokenProbeDb,
|
|
227
|
+
getConfig: resolveSearchConfig,
|
|
228
|
+
applyOrganizationScope: (query, column, scope) => this.applyOrganizationScope(
|
|
229
|
+
query as unknown as AnyBuilder,
|
|
230
|
+
column,
|
|
231
|
+
scope,
|
|
232
|
+
) as unknown as SearchTokenProbeQueryBuilder,
|
|
233
|
+
logDebug: (event, payload) => this.logSearchDebug(event, payload),
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
return this.searchAvailabilityInstance
|
|
237
|
+
}
|
|
238
|
+
|
|
177
239
|
async query<T = unknown>(entity: EntityId, opts: QueryOptions = {}): Promise<QueryResult<T>> {
|
|
178
240
|
const ext: QueryExtensionsConfig | undefined = opts.extensions
|
|
179
241
|
let hybridExtCtx: QueryExtensionContext | null = null
|
|
@@ -251,7 +313,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
251
313
|
profiler.mark('query:base_table_resolved')
|
|
252
314
|
const searchConfig = resolveSearchConfig()
|
|
253
315
|
const orgScope = this.resolveOrganizationScope(opts)
|
|
254
|
-
const searchEnabled =
|
|
316
|
+
const searchEnabled = await this.searchAvailability().staticEnabled()
|
|
255
317
|
|
|
256
318
|
const baseExists = await profiler.measure('base_table_exists', () => this.tableExists(baseTable))
|
|
257
319
|
if (!baseExists) {
|
|
@@ -416,16 +478,19 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
416
478
|
const searchSources: SearchTokenSource[] = indexSources
|
|
417
479
|
.map((src) => ({ entity: String(src.entityId), recordIdColumn: src.recordIdColumn }))
|
|
418
480
|
.filter((src) => src.recordIdColumn && src.entity)
|
|
419
|
-
const searchFilters = normalizedFilters.filter((filter) => filter.op
|
|
420
|
-
|
|
481
|
+
const searchFilters = normalizedFilters.filter((filter) => isSearchFilterOp(filter.op))
|
|
482
|
+
const sourceSearchFilters = [
|
|
483
|
+
...baseFilters,
|
|
484
|
+
...normalizedFilters.filter((filter) => String(filter.field).startsWith('cf:')),
|
|
485
|
+
].filter((filter) => isSearchFilterOp(filter.op))
|
|
486
|
+
// Probe `search_tokens` only when this query actually searches (#4723). Every consumer of
|
|
421
487
|
// `searchRuntime.enabled` already sits behind a like/ilike guard, so on a plain list load the
|
|
422
488
|
// answer is unused — and the probe is a `LIMIT 1` the planner can resolve as a seq scan over a
|
|
423
489
|
// large `search_tokens`. The join path below already probes lazily for the same reason.
|
|
424
|
-
const hasSearchTokens = searchEnabled && searchSources.length &&
|
|
425
|
-
? await this.
|
|
490
|
+
const hasSearchTokens = searchEnabled && searchSources.length && sourceSearchFilters.length
|
|
491
|
+
? await this.searchAvailability().anySourceHasTokens(searchSources, opts.tenantId ?? null, orgScope)
|
|
426
492
|
: false
|
|
427
493
|
const searchRuntime: SearchRuntime = { ...searchRuntimeBase, searchSources, enabled: searchEnabled && hasSearchTokens }
|
|
428
|
-
const joinSearchAvailability = new Map<string, boolean>()
|
|
429
494
|
if (searchFilters.length) {
|
|
430
495
|
this.logSearchDebug('search:init', {
|
|
431
496
|
entity,
|
|
@@ -476,9 +541,8 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
476
541
|
const join = joinMap.get(alias)
|
|
477
542
|
if (!join?.entityId) continue
|
|
478
543
|
const hasJoinedTokens = searchEnabled
|
|
479
|
-
? await this.
|
|
544
|
+
? await this.searchAvailability().hasTokens(String(join.entityId), opts.tenantId ?? null, orgScope)
|
|
480
545
|
: false
|
|
481
|
-
joinSearchAvailability.set(String(join.entityId), hasJoinedTokens)
|
|
482
546
|
const fallbackFields = filters
|
|
483
547
|
.filter((filter) => !hasJoinedTokens || typeof filter.value !== 'string' || tokenizeText(filter.value, searchConfig).hashes.length === 0)
|
|
484
548
|
.map((filter) => filter.column)
|
|
@@ -784,11 +848,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
784
848
|
if (!['like', 'ilike'].includes(filter.op)) return false
|
|
785
849
|
if (typeof filter.value !== 'string' || filter.value.trim().length === 0) return false
|
|
786
850
|
|
|
787
|
-
|
|
788
|
-
if (searchAvailable === undefined) {
|
|
789
|
-
searchAvailable = await this.hasSearchTokens(String(join.entityId), opts.tenantId ?? null, orgScope)
|
|
790
|
-
joinSearchAvailability.set(join.entityId, searchAvailable)
|
|
791
|
-
}
|
|
851
|
+
const searchAvailable = await this.searchAvailability().hasTokens(String(join.entityId), opts.tenantId ?? null, orgScope)
|
|
792
852
|
if (!searchAvailable) return false
|
|
793
853
|
|
|
794
854
|
const tokens = tokenizeText(String(filter.value), searchConfig)
|
|
@@ -1683,13 +1743,11 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1683
1743
|
if (!opts.tenantId) throw new Error('QueryEngine: tenantId is required')
|
|
1684
1744
|
|
|
1685
1745
|
const normalizedFilters = normalizeFilters(opts.filters)
|
|
1686
|
-
const hasSearchFilter = normalizedFilters.some((filter) => filter.op === 'like' || filter.op === 'ilike')
|
|
1687
|
-
|
|
1688
1746
|
const searchConfig = resolveSearchConfig()
|
|
1689
|
-
const searchEnabled =
|
|
1690
|
-
// Same gate as `query()
|
|
1691
|
-
const hasSearchTokens = searchEnabled && hasSearchFilter
|
|
1692
|
-
? await this.
|
|
1747
|
+
const searchEnabled = await this.searchAvailability().staticEnabled()
|
|
1748
|
+
// Same gate as `query()` (#4723): without a like/ilike filter the probe's answer is never read.
|
|
1749
|
+
const hasSearchTokens = searchEnabled && hasSearchFilter(normalizedFilters)
|
|
1750
|
+
? await this.searchAvailability().hasTokens(entity, opts.tenantId ?? null, orgScope)
|
|
1693
1751
|
: false
|
|
1694
1752
|
const searchRuntime: SearchRuntime = {
|
|
1695
1753
|
enabled: searchEnabled && hasSearchTokens,
|
|
@@ -1829,50 +1887,6 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1829
1887
|
return !!exists
|
|
1830
1888
|
}
|
|
1831
1889
|
|
|
1832
|
-
private async hasSearchTokens(
|
|
1833
|
-
entity: string,
|
|
1834
|
-
tenantId: string | null,
|
|
1835
|
-
orgScope?: { ids: string[]; includeNull: boolean } | null
|
|
1836
|
-
): Promise<boolean> {
|
|
1837
|
-
try {
|
|
1838
|
-
const db = this.getDb() as any
|
|
1839
|
-
let query = db
|
|
1840
|
-
.selectFrom('search_tokens')
|
|
1841
|
-
.select(sql<number>`1`.as('one'))
|
|
1842
|
-
.where('entity_type', '=', entity)
|
|
1843
|
-
if (tenantId !== undefined) {
|
|
1844
|
-
query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantId}`)
|
|
1845
|
-
}
|
|
1846
|
-
if (orgScope) {
|
|
1847
|
-
query = this.applyOrganizationScope(query, 'search_tokens.organization_id', orgScope)
|
|
1848
|
-
}
|
|
1849
|
-
const row = await query.limit(1).executeTakeFirst()
|
|
1850
|
-
return !!row
|
|
1851
|
-
} catch (err) {
|
|
1852
|
-
this.logSearchDebug('search:has-tokens-error', {
|
|
1853
|
-
entity, tenantId, organizationScope: orgScope,
|
|
1854
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1855
|
-
})
|
|
1856
|
-
return false
|
|
1857
|
-
}
|
|
1858
|
-
}
|
|
1859
|
-
|
|
1860
|
-
private async searchSourcesHaveTokens(
|
|
1861
|
-
sources: SearchTokenSource[],
|
|
1862
|
-
tenantId: string | null,
|
|
1863
|
-
orgScope?: { ids: string[]; includeNull: boolean } | null
|
|
1864
|
-
): Promise<boolean> {
|
|
1865
|
-
for (const source of sources) {
|
|
1866
|
-
const ok = await this.hasSearchTokens(source.entity, tenantId, orgScope)
|
|
1867
|
-
this.logSearchDebug('search:source-has-tokens', {
|
|
1868
|
-
entity: source.entity, recordIdColumn: source.recordIdColumn,
|
|
1869
|
-
tenantId, organizationScope: orgScope, hasTokens: ok,
|
|
1870
|
-
})
|
|
1871
|
-
if (ok) return true
|
|
1872
|
-
}
|
|
1873
|
-
return false
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
1890
|
private async resolveAvailableCustomFieldKeys(entityIds: string[], tenantId: string | null): Promise<string[]> {
|
|
1877
1891
|
if (!entityIds.length) return []
|
|
1878
1892
|
const cacheKey = this.customFieldKeysCacheKey(entityIds, tenantId)
|
|
@@ -2021,6 +2035,11 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
2021
2035
|
organizationId: organizationIdOverride ?? opts.organizationId ?? null,
|
|
2022
2036
|
force: false,
|
|
2023
2037
|
}
|
|
2038
|
+
const debounceMs = this.getAutoReindexDebounceMs()
|
|
2039
|
+
if (debounceMs > 0) {
|
|
2040
|
+
const key = [payload.entityType, payload.tenantId ?? '__tenant__', payload.organizationId ?? '__org__'].join('|')
|
|
2041
|
+
if (!markAutoReindexScheduled(key, debounceMs, Date.now())) return
|
|
2042
|
+
}
|
|
2024
2043
|
const context = stats
|
|
2025
2044
|
? { entity, tenantId: payload.tenantId, organizationId: payload.organizationId, baseCount: stats.baseCount, indexedCount: stats.indexedCount }
|
|
2026
2045
|
: { entity, tenantId: payload.tenantId, organizationId: payload.organizationId }
|
|
@@ -2090,6 +2109,16 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
2090
2109
|
return this.autoReindexEnabled
|
|
2091
2110
|
}
|
|
2092
2111
|
|
|
2112
|
+
private getAutoReindexDebounceMs(): number {
|
|
2113
|
+
if (this.autoReindexDebounceMs != null) return this.autoReindexDebounceMs
|
|
2114
|
+
this.autoReindexDebounceMs = parseNumberWithDefault(
|
|
2115
|
+
process.env.OM_QUERY_INDEX_AUTO_REINDEX_DEBOUNCE_MS,
|
|
2116
|
+
AUTO_REINDEX_DEBOUNCE_DEFAULT_MS,
|
|
2117
|
+
{ integer: true, min: 0 },
|
|
2118
|
+
)
|
|
2119
|
+
return this.autoReindexDebounceMs
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2093
2122
|
private isCoverageOptimizationEnabled(): boolean {
|
|
2094
2123
|
if (this.coverageOptimizationEnabled != null) return this.coverageOptimizationEnabled
|
|
2095
2124
|
const raw = (process.env.OPTIMIZE_INDEX_COVERAGE_STATS ?? '').trim().toLowerCase()
|
|
@@ -2,6 +2,7 @@ import { type Kysely, type Transaction, sql } from 'kysely'
|
|
|
2
2
|
import {
|
|
3
3
|
isSearchFieldBlocklisted,
|
|
4
4
|
resolveSearchConfig,
|
|
5
|
+
resolveSearchTokenLimits,
|
|
5
6
|
type SearchConfig,
|
|
6
7
|
} from '@open-mercato/shared/lib/search/config'
|
|
7
8
|
import { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'
|
|
@@ -79,9 +80,7 @@ function shouldIndexField(
|
|
|
79
80
|
if (lower.endsWith('_at')) return false
|
|
80
81
|
if (['created_at', 'updated_at', 'deleted_at', 'tenant_id', 'organization_id'].includes(lower)) return false
|
|
81
82
|
if (isSearchFieldBlocklisted(field, entityType, config)) return false
|
|
82
|
-
|
|
83
|
-
if (!values.length) return false
|
|
84
|
-
return values.some((text) => tokenizeText(text, config).tokens.length > 0)
|
|
83
|
+
return collectTextValues(value).some((text) => text.length > 0)
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
export function buildSearchTokenRows(params: BuildTokenOptions): SearchTokenRow[] {
|
|
@@ -95,19 +94,32 @@ export function buildSearchTokenRows(params: BuildTokenOptions): SearchTokenRow[
|
|
|
95
94
|
organizationId: params.organizationId ?? DEFAULT_SCOPE.organizationId,
|
|
96
95
|
tenantId: params.tenantId ?? DEFAULT_SCOPE.tenantId,
|
|
97
96
|
}
|
|
97
|
+
const limits = resolveSearchTokenLimits(config)
|
|
98
|
+
const recordLimit = limits.maxTokensPerRecord > 0 ? limits.maxTokensPerRecord : Number.POSITIVE_INFINITY
|
|
99
|
+
const fieldLimit = limits.maxTokensPerField > 0 ? limits.maxTokensPerField : Number.POSITIVE_INFINITY
|
|
98
100
|
|
|
99
101
|
for (const [field, rawValue] of Object.entries(params.doc)) {
|
|
102
|
+
if (tokens.length >= recordLimit) break
|
|
100
103
|
if (!shouldIndexField(field, rawValue, config, params.entityType)) continue
|
|
101
104
|
const values = collectTextValues(rawValue)
|
|
102
105
|
const seen = new Set<string>()
|
|
106
|
+
let fieldTokenCount = 0
|
|
103
107
|
for (const text of values) {
|
|
104
|
-
|
|
108
|
+
if (tokens.length >= recordLimit || fieldTokenCount >= fieldLimit) break
|
|
109
|
+
const remainingLimit = Math.min(recordLimit - tokens.length, fieldLimit - fieldTokenCount)
|
|
110
|
+
const candidateLimit = fieldTokenCount + remainingLimit
|
|
111
|
+
const tokenConfig = Number.isFinite(candidateLimit)
|
|
112
|
+
? { ...config, maxTokensPerField: candidateLimit }
|
|
113
|
+
: config
|
|
114
|
+
const { tokens: textTokens, hashes } = tokenizeText(text, tokenConfig)
|
|
105
115
|
for (let i = 0; i < textTokens.length; i += 1) {
|
|
116
|
+
if (tokens.length >= recordLimit || fieldTokenCount >= fieldLimit) break
|
|
106
117
|
const token = textTokens[i]
|
|
107
118
|
const hash = hashes[i]
|
|
108
119
|
const dedupeKey = `${field}|${hash}`
|
|
109
120
|
if (seen.has(dedupeKey)) continue
|
|
110
121
|
seen.add(dedupeKey)
|
|
122
|
+
fieldTokenCount += 1
|
|
111
123
|
debug('token.generated', { entityType: params.entityType, recordId: params.recordId, field, hash })
|
|
112
124
|
tokens.push({
|
|
113
125
|
entity_type: params.entityType,
|
|
@@ -1365,6 +1365,18 @@
|
|
|
1365
1365
|
"primary": false,
|
|
1366
1366
|
"unique": false
|
|
1367
1367
|
},
|
|
1368
|
+
{
|
|
1369
|
+
"columnNames": [
|
|
1370
|
+
"entity_type",
|
|
1371
|
+
"tenant_id",
|
|
1372
|
+
"organization_id"
|
|
1373
|
+
],
|
|
1374
|
+
"composite": true,
|
|
1375
|
+
"constraint": false,
|
|
1376
|
+
"keyName": "search_tokens_presence_idx",
|
|
1377
|
+
"primary": false,
|
|
1378
|
+
"unique": false
|
|
1379
|
+
},
|
|
1368
1380
|
{
|
|
1369
1381
|
"columnNames": [
|
|
1370
1382
|
"id"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
// Availability-probe index (.ai/specs/2026-07-31-search-token-probe-index.md).
|
|
4
|
+
// The query engines gate like/ilike routing on "does (entity_type, tenant, org)
|
|
5
|
+
// have any search tokens?". Neither existing index serves that probe as an
|
|
6
|
+
// access path — lookup_idx buries tenant_id behind field/token_hash — so on a
|
|
7
|
+
// large search_tokens the planner degraded the probe to a seq scan and the MISS
|
|
8
|
+
// answer had to exhaust every entry for the entity type (p95 12.5 s in
|
|
9
|
+
// production, #4723). With this (entity_type, tenant_id, organization_id)
|
|
10
|
+
// prefix and the index-usable `= / IS NULL` predicates the availability
|
|
11
|
+
// resolver emits, both the hit AND the miss become a B-tree prefix seek.
|
|
12
|
+
//
|
|
13
|
+
// search_tokens is high-churn, so the index is built CONCURRENTLY to avoid
|
|
14
|
+
// blocking writes during the build. CREATE INDEX CONCURRENTLY cannot run
|
|
15
|
+
// inside a transaction, hence isTransactional() => false; the migration runner
|
|
16
|
+
// applies migrations one-by-one, so this opt-out is safe (same pattern as
|
|
17
|
+
// Migration20260611103000_query_index). Drop first so retrying a failed
|
|
18
|
+
// concurrent build removes PostgreSQL's invalid index stub instead of letting
|
|
19
|
+
// IF NOT EXISTS silently accept it.
|
|
20
|
+
export class Migration20260731105052_query_index extends Migration {
|
|
21
|
+
|
|
22
|
+
override isTransactional(): boolean {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override up(): void | Promise<void> {
|
|
27
|
+
this.addSql(`drop index concurrently if exists "search_tokens_presence_idx";`);
|
|
28
|
+
this.addSql(`create index concurrently "search_tokens_presence_idx" on "search_tokens" ("entity_type", "tenant_id", "organization_id");`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
override down(): void | Promise<void> {
|
|
32
|
+
this.addSql(`drop index concurrently if exists "search_tokens_presence_idx";`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
@@ -11,6 +11,7 @@ import { getShippingAdapter } from '../../../lib/adapter-registry'
|
|
|
11
11
|
import { getShippingCarrierQueue } from '../../../lib/queue'
|
|
12
12
|
import { shippingCarriersTag } from '../../openapi'
|
|
13
13
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
14
|
+
import { readBoundedRequestBody, WebhookBodyTooLargeError } from '@open-mercato/shared/lib/webhooks'
|
|
14
15
|
|
|
15
16
|
const logger = createLogger('shipping_carriers').child({ component: 'webhook' })
|
|
16
17
|
|
|
@@ -51,7 +52,15 @@ export async function POST(req: Request, { params }: { params: Promise<{ provide
|
|
|
51
52
|
const rateLimitResponse = await checkProviderWebhookRateLimit(container, req, providerKey)
|
|
52
53
|
if (rateLimitResponse) return rateLimitResponse
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
let rawBody: string
|
|
56
|
+
try {
|
|
57
|
+
rawBody = await readBoundedRequestBody(req)
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (error instanceof WebhookBodyTooLargeError) {
|
|
60
|
+
return NextResponse.json({ error: 'Webhook payload too large' }, { status: 413 })
|
|
61
|
+
}
|
|
62
|
+
throw error
|
|
63
|
+
}
|
|
55
64
|
const headers: Record<string, string> = {}
|
|
56
65
|
req.headers.forEach((value, key) => {
|
|
57
66
|
headers[key] = value
|
|
@@ -156,6 +165,7 @@ export const openApi = {
|
|
|
156
165
|
responses: [
|
|
157
166
|
{ status: 202, description: 'Webhook accepted for async processing' },
|
|
158
167
|
{ status: 401, description: 'Signature verification failed' },
|
|
168
|
+
{ status: 413, description: 'Webhook payload too large' },
|
|
159
169
|
{ status: 404, description: 'Unknown provider' },
|
|
160
170
|
],
|
|
161
171
|
},
|