@open-mercato/core 0.6.7-develop.6706.1.b3a4c759bb → 0.6.7-develop.6725.1.07a748cfc2

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.
Files changed (111) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/admin/nav.js +14 -4
  3. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  4. package/dist/modules/auth/api/users/route.js +8 -19
  5. package/dist/modules/auth/api/users/route.js.map +2 -2
  6. package/dist/modules/auth/lib/backendChrome.js +31 -14
  7. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  8. package/dist/modules/customers/api/utils.js +12 -18
  9. package/dist/modules/customers/api/utils.js.map +2 -2
  10. package/dist/modules/customers/cli.js +11 -4
  11. package/dist/modules/customers/cli.js.map +2 -2
  12. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +4 -2
  13. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  14. package/dist/modules/customers/components/formConfig.js +14 -10
  15. package/dist/modules/customers/components/formConfig.js.map +2 -2
  16. package/dist/modules/dashboards/api/widgets/data/batch/route.js +4 -2
  17. package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
  18. package/dist/modules/dashboards/api/widgets/data/route.js +20 -2
  19. package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
  20. package/dist/modules/dashboards/lib/aggregations.js +76 -40
  21. package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
  22. package/dist/modules/dashboards/lib/exactDecimal.js +69 -0
  23. package/dist/modules/dashboards/lib/exactDecimal.js.map +7 -0
  24. package/dist/modules/dashboards/services/widgetDataService.js +228 -5
  25. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  26. package/dist/modules/feature_toggles/components/FeatureGuard.js +3 -2
  27. package/dist/modules/feature_toggles/components/FeatureGuard.js.map +2 -2
  28. package/dist/modules/feature_toggles/components/hooks/staleTime.js +5 -0
  29. package/dist/modules/feature_toggles/components/hooks/staleTime.js.map +7 -0
  30. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js +5 -2
  31. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.js.map +2 -2
  32. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js +5 -2
  33. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagJson.js.map +2 -2
  34. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js +5 -2
  35. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagNumber.js.map +2 -2
  36. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js +5 -2
  37. package/dist/modules/feature_toggles/components/hooks/useFeatureFlagString.js.map +2 -2
  38. package/dist/modules/inbox_ops/api/proposals/route.js +16 -1
  39. package/dist/modules/inbox_ops/api/proposals/route.js.map +2 -2
  40. package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js +32 -0
  41. package/dist/modules/inbox_ops/subscribers/search-reindex-proposal-created.js.map +7 -0
  42. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js +0 -5
  43. package/dist/modules/integrations/widgets/injection/external-ids/widget.client.js.map +2 -2
  44. package/dist/modules/integrations/widgets/injection/external-ids/widget.js +14 -0
  45. package/dist/modules/integrations/widgets/injection/external-ids/widget.js.map +7 -0
  46. package/dist/modules/messages/lib/searchLookup.js +8 -16
  47. package/dist/modules/messages/lib/searchLookup.js.map +2 -2
  48. package/dist/modules/query_index/api/openapi.js +13 -1
  49. package/dist/modules/query_index/api/openapi.js.map +2 -2
  50. package/dist/modules/query_index/api/status.js +165 -105
  51. package/dist/modules/query_index/api/status.js.map +2 -2
  52. package/dist/modules/query_index/components/QueryIndexesTable.js +128 -41
  53. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  54. package/dist/modules/query_index/lib/batch.js +11 -4
  55. package/dist/modules/query_index/lib/batch.js.map +2 -2
  56. package/dist/modules/query_index/lib/document.js +12 -5
  57. package/dist/modules/query_index/lib/document.js.map +2 -2
  58. package/dist/modules/query_index/lib/engine.js +36 -4
  59. package/dist/modules/query_index/lib/engine.js.map +2 -2
  60. package/dist/modules/query_index/lib/indexer.js +1 -1
  61. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  62. package/dist/modules/query_index/lib/search-tokens.js +7 -4
  63. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  64. package/dist/modules/sales/api/documents/factory.js +14 -3
  65. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  66. package/dist/modules/sales/commands/documents.js +22 -1
  67. package/dist/modules/sales/commands/documents.js.map +2 -2
  68. package/dist/modules/sales/components/useSalesChannelsEnabled.js +2 -35
  69. package/dist/modules/sales/components/useSalesChannelsEnabled.js.map +2 -2
  70. package/dist/modules/sales/data/validators.js +25 -4
  71. package/dist/modules/sales/data/validators.js.map +2 -2
  72. package/package.json +7 -7
  73. package/src/modules/auth/api/admin/nav.ts +24 -4
  74. package/src/modules/auth/api/users/route.ts +8 -28
  75. package/src/modules/auth/lib/backendChrome.tsx +51 -13
  76. package/src/modules/customers/api/utils.ts +12 -31
  77. package/src/modules/customers/cli.ts +11 -4
  78. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +4 -0
  79. package/src/modules/customers/components/formConfig.tsx +12 -8
  80. package/src/modules/dashboards/api/widgets/data/batch/route.ts +4 -0
  81. package/src/modules/dashboards/api/widgets/data/route.ts +21 -0
  82. package/src/modules/dashboards/lib/aggregations.ts +122 -43
  83. package/src/modules/dashboards/lib/exactDecimal.ts +96 -0
  84. package/src/modules/dashboards/services/widgetDataService.ts +342 -4
  85. package/src/modules/feature_toggles/components/FeatureGuard.tsx +9 -2
  86. package/src/modules/feature_toggles/components/hooks/staleTime.ts +5 -0
  87. package/src/modules/feature_toggles/components/hooks/useFeatureFlagBoolean.ts +13 -1
  88. package/src/modules/feature_toggles/components/hooks/useFeatureFlagJson.ts +12 -3
  89. package/src/modules/feature_toggles/components/hooks/useFeatureFlagNumber.ts +10 -1
  90. package/src/modules/feature_toggles/components/hooks/useFeatureFlagString.ts +10 -1
  91. package/src/modules/inbox_ops/api/proposals/route.ts +20 -1
  92. package/src/modules/inbox_ops/subscribers/search-reindex-proposal-created.ts +39 -0
  93. package/src/modules/integrations/widgets/injection/external-ids/widget.client.tsx +0 -5
  94. package/src/modules/integrations/widgets/injection/external-ids/widget.ts +13 -0
  95. package/src/modules/messages/lib/searchLookup.ts +11 -34
  96. package/src/modules/query_index/api/openapi.ts +12 -0
  97. package/src/modules/query_index/api/status.ts +238 -136
  98. package/src/modules/query_index/components/QueryIndexesTable.tsx +192 -46
  99. package/src/modules/query_index/i18n/de.json +5 -3
  100. package/src/modules/query_index/i18n/en.json +5 -3
  101. package/src/modules/query_index/i18n/es.json +5 -3
  102. package/src/modules/query_index/i18n/pl.json +5 -3
  103. package/src/modules/query_index/lib/batch.ts +11 -4
  104. package/src/modules/query_index/lib/document.ts +35 -4
  105. package/src/modules/query_index/lib/engine.ts +53 -5
  106. package/src/modules/query_index/lib/indexer.ts +1 -1
  107. package/src/modules/query_index/lib/search-tokens.ts +13 -4
  108. package/src/modules/sales/api/documents/factory.ts +28 -3
  109. package/src/modules/sales/commands/documents.ts +26 -2
  110. package/src/modules/sales/components/useSalesChannelsEnabled.ts +2 -48
  111. package/src/modules/sales/data/validators.ts +50 -5
@@ -2,9 +2,16 @@
2
2
 
3
3
  import { useQuery } from '@tanstack/react-query'
4
4
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
5
+ import { FEATURE_FLAG_STALE_TIME_MS } from './staleTime'
5
6
 
6
7
  export type UseFeatureFlagStringOptions = {
7
8
  id: string
9
+ /**
10
+ * Value used while the check is in flight and whenever the toggle cannot be
11
+ * resolved (undefined toggle, type mismatch, network or permission failure).
12
+ * Defaults to `null`.
13
+ */
14
+ defaultValue?: string | null
8
15
  }
9
16
 
10
17
  export type UseFeatureFlagStringResult = {
@@ -21,6 +28,7 @@ type Result<T> = {
21
28
  }
22
29
 
23
30
  export function useFeatureFlagString(options: UseFeatureFlagStringOptions): UseFeatureFlagStringResult {
31
+ const defaultValue = options.defaultValue ?? null
24
32
  const query = useQuery({
25
33
  queryKey: ['featureToggles', 'check', 'string', options?.id],
26
34
  queryFn: async () => {
@@ -37,9 +45,10 @@ export function useFeatureFlagString(options: UseFeatureFlagStringOptions): UseF
37
45
  return result
38
46
  },
39
47
  enabled: !!options.id,
48
+ staleTime: FEATURE_FLAG_STALE_TIME_MS,
40
49
  })
41
50
 
42
- const value = query.data?.ok ? query.data.value : null
51
+ const value = query.data?.ok ? query.data.value : defaultValue
43
52
  const isLoading = query.isLoading
44
53
 
45
54
  return {
@@ -4,6 +4,8 @@ import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
4
  import type { FilterQuery } from '@mikro-orm/postgresql'
5
5
  import { findAndCountWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
6
  import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
7
+ import { findEntityIdsBySearchTokens, type SearchTokenDatabase } from '@open-mercato/shared/lib/search/tokenLookup'
8
+ import { E } from '#generated/entities.ids.generated'
7
9
  import { InboxProposal, InboxEmail, InboxProposalAction, InboxDiscrepancy, type InboxProposalCategory } from '../../data/entities'
8
10
  import { proposalListQuerySchema } from '../../data/validators'
9
11
  import { resolveRequestContext, UnauthorizedError } from '../routeHelpers'
@@ -47,7 +49,24 @@ export async function GET(req: Request) {
47
49
  }
48
50
  }
49
51
  if (query.search) {
50
- where.summary = { $ilike: `%${escapeLikePattern(query.search)}%` }
52
+ // `summary` is covered by the inbox_ops encryption map, so an ILIKE alone
53
+ // compares the pattern against ciphertext and matches nothing once
54
+ // encryption is on. Union it with the token index, which stores hashes of
55
+ // the plaintext. Issue #2990.
56
+ const searchOr: FilterQuery<InboxProposal>[] = [
57
+ { summary: { $ilike: `%${escapeLikePattern(query.search)}%` } },
58
+ ]
59
+ const tokenMatch = await findEntityIdsBySearchTokens({
60
+ db: ctx.em.getKysely<SearchTokenDatabase>(),
61
+ entityType: E.inbox_ops.inbox_proposal,
62
+ query: query.search,
63
+ fields: ['summary'],
64
+ scope: { tenantId: ctx.tenantId, organizationId: ctx.organizationId },
65
+ })
66
+ if (tokenMatch.matched && tokenMatch.ids.length) {
67
+ searchOr.push({ id: { $in: tokenMatch.ids } })
68
+ }
69
+ where.$or = searchOr
51
70
  }
52
71
 
53
72
  const offset = (query.page - 1) * query.pageSize
@@ -0,0 +1,39 @@
1
+ export const metadata = {
2
+ event: 'inbox_ops.proposal.created',
3
+ persistent: false,
4
+ id: 'inbox_ops:query-index-reindex-proposal-created',
5
+ }
6
+
7
+ type Payload = {
8
+ proposalId?: string
9
+ tenantId?: string | null
10
+ organizationId?: string | null
11
+ }
12
+
13
+ type EventBus = { emitEvent: (name: string, body: unknown, options?: unknown) => Promise<void> }
14
+ type HandlerContext = { resolve: <T = unknown>(name: string) => T }
15
+
16
+ export default async function handle(payload: Payload, ctx: HandlerContext): Promise<void> {
17
+ const recordId = typeof payload.proposalId === 'string' ? payload.proposalId : null
18
+ const tenantId = typeof payload.tenantId === 'string' ? payload.tenantId : null
19
+ if (!recordId || !tenantId) return
20
+
21
+ let eventBus: EventBus | null = null
22
+ try {
23
+ eventBus = ctx.resolve<EventBus>('eventBus')
24
+ } catch {
25
+ return
26
+ }
27
+
28
+ await eventBus.emitEvent('query_index.upsert_one', {
29
+ entityType: 'inbox_ops:inbox_proposal',
30
+ recordId,
31
+ organizationId: payload.organizationId ?? null,
32
+ tenantId,
33
+ crudAction: 'created',
34
+ coverageBaseDelta: 1,
35
+ }, {
36
+ tenantId,
37
+ organizationId: payload.organizationId ?? null,
38
+ }).catch(() => undefined)
39
+ }
@@ -87,8 +87,3 @@ export default function ExternalIdsWidget({ data }: InjectionWidgetComponentProp
87
87
  )
88
88
  }
89
89
 
90
- ExternalIdsWidget.metadata = {
91
- id: 'integrations.injection.external-ids',
92
- title: 'External IDs',
93
- features: ['integrations.view'],
94
- }
@@ -0,0 +1,13 @@
1
+ import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'
2
+ import ExternalIdsWidget from './widget.client'
3
+
4
+ const widget: InjectionWidgetModule = {
5
+ metadata: {
6
+ id: 'integrations.injection.external-ids',
7
+ title: 'External IDs',
8
+ features: ['integrations.view'],
9
+ },
10
+ Widget: ExternalIdsWidget,
11
+ }
12
+
13
+ export default widget
@@ -1,10 +1,9 @@
1
1
  import type { EntityManager } from '@mikro-orm/postgresql'
2
- import { type Kysely, sql } from 'kysely'
3
- import { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'
4
- import { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'
2
+ import type { Kysely } from 'kysely'
3
+ import { findEntityIdsBySearchTokensCompat, type SearchTokenDatabase } from '@open-mercato/shared/lib/search/tokenLookup'
5
4
 
6
- function getDb(em: EntityManager): Kysely<any> {
7
- return em.getKysely<any>()
5
+ function getDb(em: EntityManager): Kysely<SearchTokenDatabase> {
6
+ return em.getKysely<SearchTokenDatabase>()
8
7
  }
9
8
 
10
9
  export async function findMessageIdsBySearchTokens({
@@ -20,33 +19,11 @@ export async function findMessageIdsBySearchTokens({
20
19
  organizationId: string | null
21
20
  fields?: string[]
22
21
  }): Promise<string[] | null> {
23
- const trimmed = query.trim()
24
- if (!trimmed) return null
25
-
26
- const tokens = tokenizeText(trimmed, resolveSearchConfig())
27
- if (!tokens.hashes.length) return []
28
-
29
- const db = getDb(em) as any
30
- let searchQuery = db
31
- .selectFrom('search_tokens')
32
- .select('entity_id')
33
- .where('entity_type', '=', 'messages:message')
34
- .where('field', 'in', fields)
35
- .where('token_hash', 'in', tokens.hashes)
36
- .where(sql<boolean>`tenant_id is not distinct from ${tenantId}`)
37
-
38
- if (organizationId) {
39
- searchQuery = searchQuery.where('organization_id', '=', organizationId)
40
- } else {
41
- searchQuery = searchQuery.where(sql<boolean>`organization_id is not distinct from ${null}`)
42
- }
43
-
44
- const rows = await searchQuery
45
- .groupBy('entity_id')
46
- .having(sql<boolean>`count(distinct token_hash) >= ${tokens.hashes.length}`)
47
- .execute()
48
-
49
- return rows
50
- .map((row: { entity_id?: unknown }) => (typeof row.entity_id === 'string' ? row.entity_id : null))
51
- .filter((id: string | null): id is string => typeof id === 'string' && id.length > 0)
22
+ return findEntityIdsBySearchTokensCompat({
23
+ db: getDb(em),
24
+ entityType: 'messages:message',
25
+ query,
26
+ fields,
27
+ scope: { tenantId, organizationId },
28
+ })
52
29
  }
@@ -37,13 +37,25 @@ export const queryIndexJobSchema = z.object({
37
37
 
38
38
  export const queryIndexStatusItemSchema = z.object({
39
39
  entityId: z.string(),
40
+ /** Human label. Code-defined entities have no label registry, so this mirrors `entityId`. */
40
41
  label: z.string(),
41
42
  baseCount: z.number().int().nonnegative().nullable(),
42
43
  indexCount: z.number().int().nonnegative().nullable(),
43
44
  vectorCount: z.number().int().nonnegative().nullable().optional(),
45
+ /** The entity declares `buildSource`, i.e. it is configured for vector search. */
44
46
  vectorEnabled: z.boolean().optional(),
47
+ /** Additive: vector indexing can actually run (auto-indexing on, provider reachable, tenant opted in). */
48
+ vectorIndexingActive: z.boolean().optional(),
49
+ fulltextCount: z.number().int().nonnegative().nullable().optional(),
50
+ fulltextEnabled: z.boolean().optional(),
51
+ /** Additive: the entity has active custom field definitions in the current scope. */
52
+ hasCustomFields: z.boolean().optional(),
53
+ /** Aggregate health: query index in sync AND, when vector-configured, vector coverage complete. */
45
54
  ok: z.boolean(),
55
+ /** Additive: query index alone is in sync with the base table, independent of vector/fulltext. */
56
+ queryIndexOk: z.boolean().optional(),
46
57
  job: queryIndexJobSchema,
58
+ refreshedAt: z.string().datetime().nullable().optional(),
47
59
  })
48
60
 
49
61
  export const queryIndexErrorLogSchema = z.object({