@open-mercato/core 0.4.7-develop-78d7541539 → 0.4.7-develop-74069040de

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 (187) hide show
  1. package/AGENTS.md +1 -0
  2. package/dist/modules/catalog/api/bulk-delete/route.js +86 -0
  3. package/dist/modules/catalog/api/bulk-delete/route.js.map +7 -0
  4. package/dist/modules/catalog/api/prices/route.js +39 -6
  5. package/dist/modules/catalog/api/prices/route.js.map +2 -2
  6. package/dist/modules/catalog/api/products/route.js +6 -11
  7. package/dist/modules/catalog/api/products/route.js.map +2 -2
  8. package/dist/modules/catalog/commands/products.js +2 -0
  9. package/dist/modules/catalog/commands/products.js.map +2 -2
  10. package/dist/modules/catalog/components/products/ProductsDataTable.js +9 -1
  11. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  12. package/dist/modules/catalog/lib/bulkDelete.js +70 -0
  13. package/dist/modules/catalog/lib/bulkDelete.js.map +7 -0
  14. package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js +185 -0
  15. package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js.map +7 -0
  16. package/dist/modules/catalog/widgets/injection-table.js +9 -1
  17. package/dist/modules/catalog/widgets/injection-table.js.map +2 -2
  18. package/dist/modules/catalog/workers/catalog-product-bulk-delete.js +40 -0
  19. package/dist/modules/catalog/workers/catalog-product-bulk-delete.js.map +7 -0
  20. package/dist/modules/data_sync/api/options.js +52 -0
  21. package/dist/modules/data_sync/api/options.js.map +7 -0
  22. package/dist/modules/data_sync/api/run.js +30 -35
  23. package/dist/modules/data_sync/api/run.js.map +2 -2
  24. package/dist/modules/data_sync/api/runs/[id]/cancel.js +2 -2
  25. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  26. package/dist/modules/data_sync/api/runs/[id]/retry.js +15 -30
  27. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
  28. package/dist/modules/data_sync/api/schedules/[id]/route.js +109 -0
  29. package/dist/modules/data_sync/api/schedules/[id]/route.js.map +7 -0
  30. package/dist/modules/data_sync/api/schedules/route.js +72 -0
  31. package/dist/modules/data_sync/api/schedules/route.js.map +7 -0
  32. package/dist/modules/data_sync/api/schedules/serialize.js +21 -0
  33. package/dist/modules/data_sync/api/schedules/serialize.js.map +7 -0
  34. package/dist/modules/data_sync/backend/data-sync/page.js +656 -47
  35. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  36. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +116 -34
  37. package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
  38. package/dist/modules/data_sync/components/IntegrationScheduleTab.js +394 -0
  39. package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +7 -0
  40. package/dist/modules/data_sync/data/validators.js +32 -0
  41. package/dist/modules/data_sync/data/validators.js.map +2 -2
  42. package/dist/modules/data_sync/di.js +2 -0
  43. package/dist/modules/data_sync/di.js.map +2 -2
  44. package/dist/modules/data_sync/lib/id-mapping.js +24 -2
  45. package/dist/modules/data_sync/lib/id-mapping.js.map +2 -2
  46. package/dist/modules/data_sync/lib/start-run.js +57 -0
  47. package/dist/modules/data_sync/lib/start-run.js.map +7 -0
  48. package/dist/modules/data_sync/lib/sync-engine.js +93 -4
  49. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  50. package/dist/modules/data_sync/lib/sync-run-service.js +5 -1
  51. package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
  52. package/dist/modules/data_sync/lib/sync-schedule-service.js +138 -0
  53. package/dist/modules/data_sync/lib/sync-schedule-service.js.map +7 -0
  54. package/dist/modules/data_sync/workers/sync-export.js +28 -2
  55. package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
  56. package/dist/modules/data_sync/workers/sync-import.js +28 -2
  57. package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
  58. package/dist/modules/data_sync/workers/sync-scheduled.js +5 -0
  59. package/dist/modules/data_sync/workers/sync-scheduled.js.map +2 -2
  60. package/dist/modules/entities/api/definitions.js +5 -2
  61. package/dist/modules/entities/api/definitions.js.map +2 -2
  62. package/dist/modules/entities/lib/field-definitions.js +3 -1
  63. package/dist/modules/entities/lib/field-definitions.js.map +2 -2
  64. package/dist/modules/integrations/api/[id]/route.js +14 -15
  65. package/dist/modules/integrations/api/[id]/route.js.map +2 -2
  66. package/dist/modules/integrations/api/route.js +3 -3
  67. package/dist/modules/integrations/api/route.js.map +2 -2
  68. package/dist/modules/integrations/backend/integrations/[id]/page.js +148 -33
  69. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  70. package/dist/modules/integrations/lib/state-service.js +15 -1
  71. package/dist/modules/integrations/lib/state-service.js.map +2 -2
  72. package/dist/modules/messages/api/[id]/route.js +24 -22
  73. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  74. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
  75. package/dist/modules/progress/api/active/route.js +3 -1
  76. package/dist/modules/progress/api/active/route.js.map +2 -2
  77. package/dist/modules/progress/api/jobs/[id]/route.js +1 -1
  78. package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
  79. package/dist/modules/progress/api/jobs/route.js +1 -1
  80. package/dist/modules/progress/api/jobs/route.js.map +2 -2
  81. package/dist/modules/progress/lib/events.js.map +1 -1
  82. package/dist/modules/progress/lib/progressService.js.map +2 -2
  83. package/dist/modules/progress/lib/progressServiceImpl.js +42 -1
  84. package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
  85. package/dist/modules/query_index/lib/document.js +35 -1
  86. package/dist/modules/query_index/lib/document.js.map +2 -2
  87. package/dist/modules/query_index/lib/engine.js +91 -4
  88. package/dist/modules/query_index/lib/engine.js.map +2 -2
  89. package/dist/modules/query_index/lib/indexer.js +2 -0
  90. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  91. package/dist/modules/sales/api/adjustment-kinds/route.js +3 -9
  92. package/dist/modules/sales/api/adjustment-kinds/route.js.map +2 -2
  93. package/dist/modules/sales/api/channels/route.js +3 -10
  94. package/dist/modules/sales/api/channels/route.js.map +2 -2
  95. package/dist/modules/sales/api/delivery-windows/route.js +3 -10
  96. package/dist/modules/sales/api/delivery-windows/route.js.map +2 -2
  97. package/dist/modules/sales/api/payment-methods/route.js +3 -11
  98. package/dist/modules/sales/api/payment-methods/route.js.map +2 -2
  99. package/dist/modules/sales/api/price-kinds/route.js +3 -5
  100. package/dist/modules/sales/api/price-kinds/route.js.map +2 -2
  101. package/dist/modules/sales/api/shipping-methods/route.js +3 -11
  102. package/dist/modules/sales/api/shipping-methods/route.js.map +2 -2
  103. package/dist/modules/sales/api/tags/route.js +3 -9
  104. package/dist/modules/sales/api/tags/route.js.map +2 -2
  105. package/dist/modules/sales/api/tax-rates/route.js +3 -13
  106. package/dist/modules/sales/api/tax-rates/route.js.map +2 -2
  107. package/dist/modules/sales/api/utils.js +9 -0
  108. package/dist/modules/sales/api/utils.js.map +2 -2
  109. package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +3 -9
  110. package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +2 -2
  111. package/dist/modules/workflows/api/definitions/[id]/route.js +3 -2
  112. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  113. package/dist/modules/workflows/api/definitions/route.js +4 -3
  114. package/dist/modules/workflows/api/definitions/route.js.map +2 -2
  115. package/dist/modules/workflows/api/definitions/serialize.js +25 -0
  116. package/dist/modules/workflows/api/definitions/serialize.js.map +7 -0
  117. package/package.json +3 -3
  118. package/src/modules/catalog/api/bulk-delete/route.ts +93 -0
  119. package/src/modules/catalog/api/prices/route.ts +53 -6
  120. package/src/modules/catalog/api/products/route.ts +6 -11
  121. package/src/modules/catalog/commands/products.ts +2 -0
  122. package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -0
  123. package/src/modules/catalog/i18n/de.json +10 -0
  124. package/src/modules/catalog/i18n/en.json +10 -0
  125. package/src/modules/catalog/i18n/es.json +10 -0
  126. package/src/modules/catalog/i18n/pl.json +10 -0
  127. package/src/modules/catalog/lib/bulkDelete.ts +106 -0
  128. package/src/modules/catalog/widgets/injection/product-bulk-delete/widget.ts +242 -0
  129. package/src/modules/catalog/widgets/injection-table.ts +8 -0
  130. package/src/modules/catalog/workers/catalog-product-bulk-delete.ts +48 -0
  131. package/src/modules/data_sync/AGENTS.md +11 -3
  132. package/src/modules/data_sync/api/options.ts +58 -0
  133. package/src/modules/data_sync/api/run.ts +34 -36
  134. package/src/modules/data_sync/api/runs/[id]/cancel.ts +2 -2
  135. package/src/modules/data_sync/api/runs/[id]/retry.ts +14 -31
  136. package/src/modules/data_sync/api/schedules/[id]/route.ts +130 -0
  137. package/src/modules/data_sync/api/schedules/route.ts +77 -0
  138. package/src/modules/data_sync/api/schedules/serialize.ts +31 -0
  139. package/src/modules/data_sync/backend/data-sync/page.tsx +756 -2
  140. package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +179 -53
  141. package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +512 -0
  142. package/src/modules/data_sync/data/validators.ts +35 -0
  143. package/src/modules/data_sync/di.ts +6 -0
  144. package/src/modules/data_sync/i18n/de.json +72 -0
  145. package/src/modules/data_sync/i18n/en.json +72 -0
  146. package/src/modules/data_sync/i18n/es.json +72 -0
  147. package/src/modules/data_sync/i18n/pl.json +72 -0
  148. package/src/modules/data_sync/lib/adapter.ts +4 -1
  149. package/src/modules/data_sync/lib/id-mapping.ts +32 -2
  150. package/src/modules/data_sync/lib/start-run.ts +90 -0
  151. package/src/modules/data_sync/lib/sync-engine.ts +111 -4
  152. package/src/modules/data_sync/lib/sync-run-service.ts +5 -1
  153. package/src/modules/data_sync/lib/sync-schedule-service.ts +207 -0
  154. package/src/modules/data_sync/workers/sync-export.ts +33 -2
  155. package/src/modules/data_sync/workers/sync-import.ts +33 -2
  156. package/src/modules/data_sync/workers/sync-scheduled.ts +7 -0
  157. package/src/modules/entities/api/definitions.ts +12 -2
  158. package/src/modules/entities/lib/field-definitions.ts +2 -0
  159. package/src/modules/integrations/AGENTS.md +16 -3
  160. package/src/modules/integrations/api/[id]/route.ts +14 -15
  161. package/src/modules/integrations/api/route.ts +3 -3
  162. package/src/modules/integrations/backend/integrations/[id]/page.tsx +176 -54
  163. package/src/modules/integrations/lib/state-service.ts +25 -1
  164. package/src/modules/messages/api/[id]/route.ts +25 -22
  165. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +3 -3
  166. package/src/modules/progress/api/active/route.ts +4 -1
  167. package/src/modules/progress/api/jobs/[id]/route.ts +1 -1
  168. package/src/modules/progress/api/jobs/route.ts +1 -1
  169. package/src/modules/progress/lib/events.ts +6 -0
  170. package/src/modules/progress/lib/progressService.ts +1 -0
  171. package/src/modules/progress/lib/progressServiceImpl.ts +47 -1
  172. package/src/modules/query_index/lib/document.ts +52 -1
  173. package/src/modules/query_index/lib/engine.ts +104 -4
  174. package/src/modules/query_index/lib/indexer.ts +2 -0
  175. package/src/modules/sales/api/adjustment-kinds/route.ts +3 -9
  176. package/src/modules/sales/api/channels/route.ts +3 -10
  177. package/src/modules/sales/api/delivery-windows/route.ts +3 -10
  178. package/src/modules/sales/api/payment-methods/route.ts +3 -11
  179. package/src/modules/sales/api/price-kinds/route.ts +3 -5
  180. package/src/modules/sales/api/shipping-methods/route.ts +3 -11
  181. package/src/modules/sales/api/tags/route.ts +3 -9
  182. package/src/modules/sales/api/tax-rates/route.ts +3 -13
  183. package/src/modules/sales/api/utils.ts +9 -0
  184. package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +3 -9
  185. package/src/modules/workflows/api/definitions/[id]/route.ts +3 -2
  186. package/src/modules/workflows/api/definitions/route.ts +4 -3
  187. package/src/modules/workflows/api/definitions/serialize.ts +23 -0
@@ -1,3 +1,4 @@
1
+ const AGGREGATE_SEARCH_FIELD = "search_text";
1
2
  function normalizeScopeValue(value) {
2
3
  if (value === void 0 || value === null || value === "") return null;
3
4
  return value;
@@ -10,6 +11,37 @@ function normalizeValue(value) {
10
11
  if (value === void 0) return null;
11
12
  return value;
12
13
  }
14
+ function collectAggregateSearchValues(field, value) {
15
+ const lower = field.toLowerCase();
16
+ if (lower === AGGREGATE_SEARCH_FIELD || lower === "id" || lower.endsWith("_id") || lower.endsWith(".id") || lower.endsWith("_at") || ["created_at", "updated_at", "deleted_at", "tenant_id", "organization_id"].includes(lower)) {
17
+ return [];
18
+ }
19
+ if (typeof value === "string") {
20
+ const trimmed = value.trim();
21
+ return trimmed.length > 0 ? [trimmed] : [];
22
+ }
23
+ if (Array.isArray(value)) {
24
+ return value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
25
+ }
26
+ return [];
27
+ }
28
+ function attachAggregateSearchField(doc) {
29
+ const parts = [];
30
+ const seen = /* @__PURE__ */ new Set();
31
+ for (const [field, value] of Object.entries(doc)) {
32
+ const values = collectAggregateSearchValues(field, value);
33
+ for (const entry of values) {
34
+ const key = entry.toLowerCase();
35
+ if (seen.has(key)) continue;
36
+ seen.add(key);
37
+ parts.push(entry);
38
+ }
39
+ }
40
+ if (parts.length > 0) {
41
+ doc[AGGREGATE_SEARCH_FIELD] = parts.join("\n");
42
+ }
43
+ return doc;
44
+ }
13
45
  function buildIndexDocument(baseRow, customFieldValues = [], scope = {}) {
14
46
  const doc = {};
15
47
  for (const [key, value] of Object.entries(baseRow)) {
@@ -43,9 +75,11 @@ function buildIndexDocument(baseRow, customFieldValues = [], scope = {}) {
43
75
  doc[key] = values;
44
76
  }
45
77
  }
46
- return doc;
78
+ return attachAggregateSearchField(doc);
47
79
  }
48
80
  export {
81
+ AGGREGATE_SEARCH_FIELD,
82
+ attachAggregateSearchField,
49
83
  buildIndexDocument
50
84
  };
51
85
  //# sourceMappingURL=document.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/query_index/lib/document.ts"],
4
- "sourcesContent": ["export type IndexDocumentScope = {\n organizationId?: string | null\n tenantId?: string | null\n}\n\nexport type IndexCustomFieldValue = {\n key: string\n value: unknown\n organizationId?: string | null\n tenantId?: string | null\n}\n\nfunction normalizeScopeValue(value: string | null | undefined): string | null {\n if (value === undefined || value === null || value === '') return null\n return value\n}\n\nfunction isScopedValueVisible(\n scopeValue: string | null,\n fieldValue: string | null,\n): boolean {\n if (scopeValue === null) return fieldValue === null\n return fieldValue === null || fieldValue === scopeValue\n}\n\nfunction normalizeValue(value: unknown): unknown {\n if (value === undefined) return null\n return value\n}\n\nexport function buildIndexDocument(\n baseRow: Record<string, unknown>,\n customFieldValues: Iterable<IndexCustomFieldValue> = [],\n scope: IndexDocumentScope = {},\n): Record<string, unknown> {\n const doc: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(baseRow)) {\n doc[key] = value\n }\n\n const scopeOrg = normalizeScopeValue(scope.organizationId ?? null)\n const scopeTenant = normalizeScopeValue(scope.tenantId ?? null)\n\n const grouped = new Map<string, unknown[]>()\n for (const field of customFieldValues) {\n const org = normalizeScopeValue(field.organizationId ?? null)\n const tenant = normalizeScopeValue(field.tenantId ?? null)\n\n if (!isScopedValueVisible(scopeOrg, org)) continue\n if (!isScopedValueVisible(scopeTenant, tenant)) continue\n\n const bucketKey = `cf:${field.key}`\n let bucket = grouped.get(bucketKey)\n if (!bucket) {\n bucket = []\n grouped.set(bucketKey, bucket)\n }\n\n const { value } = field\n if (Array.isArray(value)) {\n for (const entry of value) bucket.push(normalizeValue(entry))\n } else {\n bucket.push(normalizeValue(value))\n }\n }\n\n for (const [key, values] of grouped.entries()) {\n if (values.length === 1) {\n doc[key] = values[0]\n } else if (values.length > 1) {\n doc[key] = values\n }\n }\n\n return doc\n}\n"],
5
- "mappings": "AAYA,SAAS,oBAAoB,OAAiD;AAC5E,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI,QAAO;AAClE,SAAO;AACT;AAEA,SAAS,qBACP,YACA,YACS;AACT,MAAI,eAAe,KAAM,QAAO,eAAe;AAC/C,SAAO,eAAe,QAAQ,eAAe;AAC/C;AAEA,SAAS,eAAe,OAAyB;AAC/C,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,mBACd,SACA,oBAAqD,CAAC,GACtD,QAA4B,CAAC,GACJ;AACzB,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,GAAG,IAAI;AAAA,EACb;AAEA,QAAM,WAAW,oBAAoB,MAAM,kBAAkB,IAAI;AACjE,QAAM,cAAc,oBAAoB,MAAM,YAAY,IAAI;AAE9D,QAAM,UAAU,oBAAI,IAAuB;AAC3C,aAAW,SAAS,mBAAmB;AACrC,UAAM,MAAM,oBAAoB,MAAM,kBAAkB,IAAI;AAC5D,UAAM,SAAS,oBAAoB,MAAM,YAAY,IAAI;AAEzD,QAAI,CAAC,qBAAqB,UAAU,GAAG,EAAG;AAC1C,QAAI,CAAC,qBAAqB,aAAa,MAAM,EAAG;AAEhD,UAAM,YAAY,MAAM,MAAM,GAAG;AACjC,QAAI,SAAS,QAAQ,IAAI,SAAS;AAClC,QAAI,CAAC,QAAQ;AACX,eAAS,CAAC;AACV,cAAQ,IAAI,WAAW,MAAM;AAAA,IAC/B;AAEA,UAAM,EAAE,MAAM,IAAI;AAClB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,SAAS,MAAO,QAAO,KAAK,eAAe,KAAK,CAAC;AAAA,IAC9D,OAAO;AACL,aAAO,KAAK,eAAe,KAAK,CAAC;AAAA,IACnC;AAAA,EACF;AAEA,aAAW,CAAC,KAAK,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC7C,QAAI,OAAO,WAAW,GAAG;AACvB,UAAI,GAAG,IAAI,OAAO,CAAC;AAAA,IACrB,WAAW,OAAO,SAAS,GAAG;AAC5B,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AAEA,SAAO;AACT;",
4
+ "sourcesContent": ["export type IndexDocumentScope = {\n organizationId?: string | null\n tenantId?: string | null\n}\n\nexport type IndexCustomFieldValue = {\n key: string\n value: unknown\n organizationId?: string | null\n tenantId?: string | null\n}\n\nexport const AGGREGATE_SEARCH_FIELD = 'search_text'\n\nfunction normalizeScopeValue(value: string | null | undefined): string | null {\n if (value === undefined || value === null || value === '') return null\n return value\n}\n\nfunction isScopedValueVisible(\n scopeValue: string | null,\n fieldValue: string | null,\n): boolean {\n if (scopeValue === null) return fieldValue === null\n return fieldValue === null || fieldValue === scopeValue\n}\n\nfunction normalizeValue(value: unknown): unknown {\n if (value === undefined) return null\n return value\n}\n\nfunction collectAggregateSearchValues(field: string, value: unknown): string[] {\n const lower = field.toLowerCase()\n if (\n lower === AGGREGATE_SEARCH_FIELD\n || lower === 'id'\n || lower.endsWith('_id')\n || lower.endsWith('.id')\n || lower.endsWith('_at')\n || ['created_at', 'updated_at', 'deleted_at', 'tenant_id', 'organization_id'].includes(lower)\n ) {\n return []\n }\n\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length > 0 ? [trimmed] : []\n }\n\n if (Array.isArray(value)) {\n return value\n .filter((entry): entry is string => typeof entry === 'string')\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0)\n }\n\n return []\n}\n\nexport function attachAggregateSearchField(doc: Record<string, unknown>): Record<string, unknown> {\n const parts: string[] = []\n const seen = new Set<string>()\n\n for (const [field, value] of Object.entries(doc)) {\n const values = collectAggregateSearchValues(field, value)\n for (const entry of values) {\n const key = entry.toLowerCase()\n if (seen.has(key)) continue\n seen.add(key)\n parts.push(entry)\n }\n }\n\n if (parts.length > 0) {\n doc[AGGREGATE_SEARCH_FIELD] = parts.join('\\n')\n }\n\n return doc\n}\n\nexport function buildIndexDocument(\n baseRow: Record<string, unknown>,\n customFieldValues: Iterable<IndexCustomFieldValue> = [],\n scope: IndexDocumentScope = {},\n): Record<string, unknown> {\n const doc: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(baseRow)) {\n doc[key] = value\n }\n\n const scopeOrg = normalizeScopeValue(scope.organizationId ?? null)\n const scopeTenant = normalizeScopeValue(scope.tenantId ?? null)\n\n const grouped = new Map<string, unknown[]>()\n for (const field of customFieldValues) {\n const org = normalizeScopeValue(field.organizationId ?? null)\n const tenant = normalizeScopeValue(field.tenantId ?? null)\n\n if (!isScopedValueVisible(scopeOrg, org)) continue\n if (!isScopedValueVisible(scopeTenant, tenant)) continue\n\n const bucketKey = `cf:${field.key}`\n let bucket = grouped.get(bucketKey)\n if (!bucket) {\n bucket = []\n grouped.set(bucketKey, bucket)\n }\n\n const { value } = field\n if (Array.isArray(value)) {\n for (const entry of value) bucket.push(normalizeValue(entry))\n } else {\n bucket.push(normalizeValue(value))\n }\n }\n\n for (const [key, values] of grouped.entries()) {\n if (values.length === 1) {\n doc[key] = values[0]\n } else if (values.length > 1) {\n doc[key] = values\n }\n }\n\n return attachAggregateSearchField(doc)\n}\n"],
5
+ "mappings": "AAYO,MAAM,yBAAyB;AAEtC,SAAS,oBAAoB,OAAiD;AAC5E,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI,QAAO;AAClE,SAAO;AACT;AAEA,SAAS,qBACP,YACA,YACS;AACT,MAAI,eAAe,KAAM,QAAO,eAAe;AAC/C,SAAO,eAAe,QAAQ,eAAe;AAC/C;AAEA,SAAS,eAAe,OAAyB;AAC/C,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO;AACT;AAEA,SAAS,6BAA6B,OAAe,OAA0B;AAC7E,QAAM,QAAQ,MAAM,YAAY;AAChC,MACE,UAAU,0BACP,UAAU,QACV,MAAM,SAAS,KAAK,KACpB,MAAM,SAAS,KAAK,KACpB,MAAM,SAAS,KAAK,KACpB,CAAC,cAAc,cAAc,cAAc,aAAa,iBAAiB,EAAE,SAAS,KAAK,GAC5F;AACA,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,UAAU,MAAM,KAAK;AAC3B,WAAO,QAAQ,SAAS,IAAI,CAAC,OAAO,IAAI,CAAC;AAAA,EAC3C;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MACJ,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ,EAC5D,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAAA,EACvC;AAEA,SAAO,CAAC;AACV;AAEO,SAAS,2BAA2B,KAAuD;AAChG,QAAM,QAAkB,CAAC;AACzB,QAAM,OAAO,oBAAI,IAAY;AAE7B,aAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAChD,UAAM,SAAS,6BAA6B,OAAO,KAAK;AACxD,eAAW,SAAS,QAAQ;AAC1B,YAAM,MAAM,MAAM,YAAY;AAC9B,UAAI,KAAK,IAAI,GAAG,EAAG;AACnB,WAAK,IAAI,GAAG;AACZ,YAAM,KAAK,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,MAAM,SAAS,GAAG;AACpB,QAAI,sBAAsB,IAAI,MAAM,KAAK,IAAI;AAAA,EAC/C;AAEA,SAAO;AACT;AAEO,SAAS,mBACd,SACA,oBAAqD,CAAC,GACtD,QAA4B,CAAC,GACJ;AACzB,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,GAAG,IAAI;AAAA,EACb;AAEA,QAAM,WAAW,oBAAoB,MAAM,kBAAkB,IAAI;AACjE,QAAM,cAAc,oBAAoB,MAAM,YAAY,IAAI;AAE9D,QAAM,UAAU,oBAAI,IAAuB;AAC3C,aAAW,SAAS,mBAAmB;AACrC,UAAM,MAAM,oBAAoB,MAAM,kBAAkB,IAAI;AAC5D,UAAM,SAAS,oBAAoB,MAAM,YAAY,IAAI;AAEzD,QAAI,CAAC,qBAAqB,UAAU,GAAG,EAAG;AAC1C,QAAI,CAAC,qBAAqB,aAAa,MAAM,EAAG;AAEhD,UAAM,YAAY,MAAM,MAAM,GAAG;AACjC,QAAI,SAAS,QAAQ,IAAI,SAAS;AAClC,QAAI,CAAC,QAAQ;AACX,eAAS,CAAC;AACV,cAAQ,IAAI,WAAW,MAAM;AAAA,IAC/B;AAEA,UAAM,EAAE,MAAM,IAAI;AAClB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,SAAS,MAAO,QAAO,KAAK,eAAe,KAAK,CAAC;AAAA,IAC9D,OAAO;AACL,aAAO,KAAK,eAAe,KAAK,CAAC;AAAA,IACnC;AAAA,EACF;AAEA,aAAW,CAAC,KAAK,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC7C,QAAI,OAAO,WAAW,GAAG;AACvB,UAAI,GAAG,IAAI,OAAO,CAAC;AAAA,IACrB,WAAW,OAAO,SAAS,GAAG;AAC5B,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AAEA,SAAO,2BAA2B,GAAG;AACvC;",
6
6
  "names": []
7
7
  }
@@ -461,14 +461,41 @@ class HybridQueryEngine {
461
461
  );
462
462
  }
463
463
  for (const filter of baseFilters) {
464
- const baseField = resolveBaseColumn(String(filter.field));
465
- if (!baseField) continue;
464
+ const fieldName = String(filter.field);
465
+ const baseField = resolveBaseColumn(fieldName);
466
+ if (!baseField) {
467
+ builder = this.applyIndexDocFilterFromAlias(
468
+ knex,
469
+ builder,
470
+ "ei",
471
+ entity,
472
+ fieldName,
473
+ filter.op,
474
+ filter.value,
475
+ "b.id",
476
+ searchRuntime
477
+ );
478
+ if (optimizedCountBuilder) {
479
+ optimizedCountBuilder = this.applyIndexDocFilterFromAlias(
480
+ knex,
481
+ optimizedCountBuilder,
482
+ "ei",
483
+ entity,
484
+ fieldName,
485
+ filter.op,
486
+ filter.value,
487
+ "b.id",
488
+ searchRuntime
489
+ );
490
+ }
491
+ continue;
492
+ }
466
493
  const column = qualify(baseField);
467
494
  builder = this.applyColumnFilter(builder, column, filter, {
468
495
  ...searchRuntime,
469
496
  knex,
470
497
  entity,
471
- field: String(filter.field),
498
+ field: fieldName,
472
499
  recordIdColumn: "b.id"
473
500
  });
474
501
  if (optimizedCountBuilder) {
@@ -476,7 +503,7 @@ class HybridQueryEngine {
476
503
  ...searchRuntime,
477
504
  knex,
478
505
  entity,
479
- field: String(filter.field),
506
+ field: fieldName,
480
507
  recordIdColumn: "b.id"
481
508
  });
482
509
  }
@@ -961,6 +988,66 @@ class HybridQueryEngine {
961
988
  return q;
962
989
  }
963
990
  }
991
+ applyIndexDocFilterFromAlias(knex, q, alias, entityType, key, op, value, recordIdColumn, search) {
992
+ const text = knex.raw(`(${alias}.doc ->> ?)`, [key]);
993
+ if ((op === "like" || op === "ilike") && search?.enabled && typeof value === "string") {
994
+ const tokens = tokenizeText(String(value), search.config);
995
+ const hashes = tokens.hashes;
996
+ if (hashes.length) {
997
+ const applied = this.applySearchTokens(q, {
998
+ knex,
999
+ entity: entityType,
1000
+ field: key,
1001
+ hashes,
1002
+ recordIdColumn,
1003
+ tenantId: search.tenantId ?? null,
1004
+ organizationScope: search.organizationScope ?? null
1005
+ });
1006
+ this.logSearchDebug("search:index-doc-filter", {
1007
+ entity: entityType,
1008
+ field: key,
1009
+ tokens: tokens.tokens,
1010
+ hashes,
1011
+ applied,
1012
+ tenantId: search.tenantId ?? null,
1013
+ organizationScope: search.organizationScope
1014
+ });
1015
+ if (applied) return q;
1016
+ } else {
1017
+ this.logSearchDebug("search:index-doc-skip-empty-hashes", {
1018
+ entity: entityType,
1019
+ field: key,
1020
+ value
1021
+ });
1022
+ }
1023
+ return q;
1024
+ }
1025
+ switch (op) {
1026
+ case "eq":
1027
+ return q.where(text, "=", value);
1028
+ case "ne":
1029
+ return q.where(text, "!=", value);
1030
+ case "in":
1031
+ return q.whereIn(text, this.toArray(value));
1032
+ case "nin":
1033
+ return q.whereNotIn(text, this.toArray(value));
1034
+ case "like":
1035
+ return q.where(text, "like", value);
1036
+ case "ilike":
1037
+ return q.where(text, "ilike", value);
1038
+ case "exists":
1039
+ return value ? q.whereRaw(`${text.toString()} is not null`) : q.whereRaw(`${text.toString()} is null`);
1040
+ case "gt":
1041
+ case "gte":
1042
+ case "lt":
1043
+ case "lte": {
1044
+ const operator = op === "gt" ? ">" : op === "gte" ? ">=" : op === "lt" ? "<" : "<=";
1045
+ return q.where(text, operator, value);
1046
+ }
1047
+ default:
1048
+ return q;
1049
+ }
1050
+ }
964
1051
  async queryCustomEntity(entity, opts = {}) {
965
1052
  const knex = this.getKnex();
966
1053
  const alias = "ce";