@open-mercato/shared 0.4.2-canary-c02407ff85

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 (324) hide show
  1. package/build.mjs +101 -0
  2. package/dist/index.js +1 -0
  3. package/dist/index.js.map +7 -0
  4. package/dist/lib/api/crud.js +47 -0
  5. package/dist/lib/api/crud.js.map +7 -0
  6. package/dist/lib/api/scoped.js +140 -0
  7. package/dist/lib/api/scoped.js.map +7 -0
  8. package/dist/lib/auth/jwt.js +34 -0
  9. package/dist/lib/auth/jwt.js.map +7 -0
  10. package/dist/lib/auth/server.js +157 -0
  11. package/dist/lib/auth/server.js.map +7 -0
  12. package/dist/lib/boolean.js +22 -0
  13. package/dist/lib/boolean.js.map +7 -0
  14. package/dist/lib/bootstrap/appResolver.js +43 -0
  15. package/dist/lib/bootstrap/appResolver.js.map +7 -0
  16. package/dist/lib/bootstrap/dynamicLoader.js +108 -0
  17. package/dist/lib/bootstrap/dynamicLoader.js.map +7 -0
  18. package/dist/lib/bootstrap/factory.js +59 -0
  19. package/dist/lib/bootstrap/factory.js.map +7 -0
  20. package/dist/lib/bootstrap/index.js +11 -0
  21. package/dist/lib/bootstrap/index.js.map +7 -0
  22. package/dist/lib/bootstrap/types.js +1 -0
  23. package/dist/lib/bootstrap/types.js.map +7 -0
  24. package/dist/lib/cache/segments.js +36 -0
  25. package/dist/lib/cache/segments.js.map +7 -0
  26. package/dist/lib/cli/progress.js +46 -0
  27. package/dist/lib/cli/progress.js.map +7 -0
  28. package/dist/lib/commands/command-bus.js +285 -0
  29. package/dist/lib/commands/command-bus.js.map +7 -0
  30. package/dist/lib/commands/customFieldSnapshots.js +66 -0
  31. package/dist/lib/commands/customFieldSnapshots.js.map +7 -0
  32. package/dist/lib/commands/helpers.js +98 -0
  33. package/dist/lib/commands/helpers.js.map +7 -0
  34. package/dist/lib/commands/index.js +8 -0
  35. package/dist/lib/commands/index.js.map +7 -0
  36. package/dist/lib/commands/operationMetadata.js +32 -0
  37. package/dist/lib/commands/operationMetadata.js.map +7 -0
  38. package/dist/lib/commands/registry.js +43 -0
  39. package/dist/lib/commands/registry.js.map +7 -0
  40. package/dist/lib/commands/scope.js +44 -0
  41. package/dist/lib/commands/scope.js.map +7 -0
  42. package/dist/lib/commands/types.js +8 -0
  43. package/dist/lib/commands/types.js.map +7 -0
  44. package/dist/lib/crud/cache-stats.js +98 -0
  45. package/dist/lib/crud/cache-stats.js.map +7 -0
  46. package/dist/lib/crud/cache.js +175 -0
  47. package/dist/lib/crud/cache.js.map +7 -0
  48. package/dist/lib/crud/custom-fields-client.js +52 -0
  49. package/dist/lib/crud/custom-fields-client.js.map +7 -0
  50. package/dist/lib/crud/custom-fields.js +467 -0
  51. package/dist/lib/crud/custom-fields.js.map +7 -0
  52. package/dist/lib/crud/errors.js +24 -0
  53. package/dist/lib/crud/errors.js.map +7 -0
  54. package/dist/lib/crud/exporters.js +154 -0
  55. package/dist/lib/crud/exporters.js.map +7 -0
  56. package/dist/lib/crud/factory.js +1311 -0
  57. package/dist/lib/crud/factory.js.map +7 -0
  58. package/dist/lib/crud/types.js +1 -0
  59. package/dist/lib/crud/types.js.map +7 -0
  60. package/dist/lib/custom-fields/normalize.js +36 -0
  61. package/dist/lib/custom-fields/normalize.js.map +7 -0
  62. package/dist/lib/data/engine.js +396 -0
  63. package/dist/lib/data/engine.js.map +7 -0
  64. package/dist/lib/db/escapeLikePattern.js +5 -0
  65. package/dist/lib/db/escapeLikePattern.js.map +7 -0
  66. package/dist/lib/db/mikro.js +82 -0
  67. package/dist/lib/db/mikro.js.map +7 -0
  68. package/dist/lib/di/container.js +94 -0
  69. package/dist/lib/di/container.js.map +7 -0
  70. package/dist/lib/email/send.js +12 -0
  71. package/dist/lib/email/send.js.map +7 -0
  72. package/dist/lib/encryption/aes.js +58 -0
  73. package/dist/lib/encryption/aes.js.map +7 -0
  74. package/dist/lib/encryption/customFieldValues.js +49 -0
  75. package/dist/lib/encryption/customFieldValues.js.map +7 -0
  76. package/dist/lib/encryption/entityFields.js +26 -0
  77. package/dist/lib/encryption/entityFields.js.map +7 -0
  78. package/dist/lib/encryption/entityIds.js +80 -0
  79. package/dist/lib/encryption/entityIds.js.map +7 -0
  80. package/dist/lib/encryption/find.js +45 -0
  81. package/dist/lib/encryption/find.js.map +7 -0
  82. package/dist/lib/encryption/indexDoc.js +69 -0
  83. package/dist/lib/encryption/indexDoc.js.map +7 -0
  84. package/dist/lib/encryption/kms.js +282 -0
  85. package/dist/lib/encryption/kms.js.map +7 -0
  86. package/dist/lib/encryption/subscriber.js +330 -0
  87. package/dist/lib/encryption/subscriber.js.map +7 -0
  88. package/dist/lib/encryption/tenantDataEncryptionService.js +252 -0
  89. package/dist/lib/encryption/tenantDataEncryptionService.js.map +7 -0
  90. package/dist/lib/encryption/toggles.js +18 -0
  91. package/dist/lib/encryption/toggles.js.map +7 -0
  92. package/dist/lib/entities/naming.js +9 -0
  93. package/dist/lib/entities/naming.js.map +7 -0
  94. package/dist/lib/entities/system-entities.js +43 -0
  95. package/dist/lib/entities/system-entities.js.map +7 -0
  96. package/dist/lib/frontend/organizationEvents.js +41 -0
  97. package/dist/lib/frontend/organizationEvents.js.map +7 -0
  98. package/dist/lib/frontend/useOrganizationScope.js +32 -0
  99. package/dist/lib/frontend/useOrganizationScope.js.map +7 -0
  100. package/dist/lib/hotkeys/index.js +128 -0
  101. package/dist/lib/hotkeys/index.js.map +7 -0
  102. package/dist/lib/i18n/app-dictionaries.js +17 -0
  103. package/dist/lib/i18n/app-dictionaries.js.map +7 -0
  104. package/dist/lib/i18n/config.js +7 -0
  105. package/dist/lib/i18n/config.js.map +7 -0
  106. package/dist/lib/i18n/context.js +50 -0
  107. package/dist/lib/i18n/context.js.map +7 -0
  108. package/dist/lib/i18n/server.js +68 -0
  109. package/dist/lib/i18n/server.js.map +7 -0
  110. package/dist/lib/i18n/translate.js +45 -0
  111. package/dist/lib/i18n/translate.js.map +7 -0
  112. package/dist/lib/indexers/error-log.js +82 -0
  113. package/dist/lib/indexers/error-log.js.map +7 -0
  114. package/dist/lib/indexers/status-log.js +80 -0
  115. package/dist/lib/indexers/status-log.js.map +7 -0
  116. package/dist/lib/lib/auth/jwt.js +34 -0
  117. package/dist/lib/lib/auth/jwt.js.map +7 -0
  118. package/dist/lib/lib/auth/server.js +77 -0
  119. package/dist/lib/lib/auth/server.js.map +7 -0
  120. package/dist/lib/lib/email/send.js +12 -0
  121. package/dist/lib/lib/email/send.js.map +7 -0
  122. package/dist/lib/lib/i18n/config.js +7 -0
  123. package/dist/lib/lib/i18n/config.js.map +7 -0
  124. package/dist/lib/lib/i18n/context.js +31 -0
  125. package/dist/lib/lib/i18n/context.js.map +7 -0
  126. package/dist/lib/lib/utils.js +9 -0
  127. package/dist/lib/lib/utils.js.map +7 -0
  128. package/dist/lib/location/countries.js +68 -0
  129. package/dist/lib/location/countries.js.map +7 -0
  130. package/dist/lib/modules/index.js +6 -0
  131. package/dist/lib/modules/index.js.map +7 -0
  132. package/dist/lib/modules/registry.js +18 -0
  133. package/dist/lib/modules/registry.js.map +7 -0
  134. package/dist/lib/openapi/crud.js +137 -0
  135. package/dist/lib/openapi/crud.js.map +7 -0
  136. package/dist/lib/openapi/generator.js +1131 -0
  137. package/dist/lib/openapi/generator.js.map +7 -0
  138. package/dist/lib/openapi/index.js +10 -0
  139. package/dist/lib/openapi/index.js.map +7 -0
  140. package/dist/lib/openapi/sanitize.js +110 -0
  141. package/dist/lib/openapi/sanitize.js.map +7 -0
  142. package/dist/lib/openapi/types.js +1 -0
  143. package/dist/lib/openapi/types.js.map +7 -0
  144. package/dist/lib/profiler/index.js +258 -0
  145. package/dist/lib/profiler/index.js.map +7 -0
  146. package/dist/lib/query/engine.js +729 -0
  147. package/dist/lib/query/engine.js.map +7 -0
  148. package/dist/lib/query/join-utils.js +195 -0
  149. package/dist/lib/query/join-utils.js.map +7 -0
  150. package/dist/lib/query/types.js +9 -0
  151. package/dist/lib/query/types.js.map +7 -0
  152. package/dist/lib/search/config.js +32 -0
  153. package/dist/lib/search/config.js.map +7 -0
  154. package/dist/lib/search/tokenize.js +34 -0
  155. package/dist/lib/search/tokenize.js.map +7 -0
  156. package/dist/lib/slugify.js +24 -0
  157. package/dist/lib/slugify.js.map +7 -0
  158. package/dist/lib/testing/bootstrap.js +51 -0
  159. package/dist/lib/testing/bootstrap.js.map +7 -0
  160. package/dist/lib/testing/index.js +17 -0
  161. package/dist/lib/testing/index.js.map +7 -0
  162. package/dist/lib/testing/renderWithProviders.js +15 -0
  163. package/dist/lib/testing/renderWithProviders.js.map +7 -0
  164. package/dist/lib/url.js +12 -0
  165. package/dist/lib/url.js.map +7 -0
  166. package/dist/lib/utils.js +13 -0
  167. package/dist/lib/utils.js.map +7 -0
  168. package/dist/lib/version.js +7 -0
  169. package/dist/lib/version.js.map +7 -0
  170. package/dist/modules/dashboard/widgets.js +1 -0
  171. package/dist/modules/dashboard/widgets.js.map +7 -0
  172. package/dist/modules/dsl.js +30 -0
  173. package/dist/modules/dsl.js.map +7 -0
  174. package/dist/modules/entities/kinds.js +22 -0
  175. package/dist/modules/entities/kinds.js.map +7 -0
  176. package/dist/modules/entities/options.js +26 -0
  177. package/dist/modules/entities/options.js.map +7 -0
  178. package/dist/modules/entities/validation.js +102 -0
  179. package/dist/modules/entities/validation.js.map +7 -0
  180. package/dist/modules/entities/validators.js +88 -0
  181. package/dist/modules/entities/validators.js.map +7 -0
  182. package/dist/modules/entities.js +1 -0
  183. package/dist/modules/entities.js.map +7 -0
  184. package/dist/modules/navigation/sidebarPreferences.js +50 -0
  185. package/dist/modules/navigation/sidebarPreferences.js.map +7 -0
  186. package/dist/modules/perspectives/types.js +1 -0
  187. package/dist/modules/perspectives/types.js.map +7 -0
  188. package/dist/modules/registry.js +96 -0
  189. package/dist/modules/registry.js.map +7 -0
  190. package/dist/modules/search.js +15 -0
  191. package/dist/modules/search.js.map +7 -0
  192. package/dist/modules/vector.js +1 -0
  193. package/dist/modules/vector.js.map +7 -0
  194. package/dist/modules/widgets/injection-loader.js +180 -0
  195. package/dist/modules/widgets/injection-loader.js.map +7 -0
  196. package/dist/modules/widgets/injection.js +1 -0
  197. package/dist/modules/widgets/injection.js.map +7 -0
  198. package/dist/security/features.js +23 -0
  199. package/dist/security/features.js.map +7 -0
  200. package/dist/types/pg.d.js +1 -0
  201. package/dist/types/pg.d.js.map +7 -0
  202. package/dist/types/react-email.d.js +1 -0
  203. package/dist/types/react-email.d.js.map +7 -0
  204. package/dist/types/resend.d.js +1 -0
  205. package/dist/types/resend.d.js.map +7 -0
  206. package/jest.config.cjs +22 -0
  207. package/package.json +88 -0
  208. package/src/index.ts +0 -0
  209. package/src/lib/api/__tests__/scoped.test.ts +38 -0
  210. package/src/lib/api/crud.ts +59 -0
  211. package/src/lib/api/scoped.ts +239 -0
  212. package/src/lib/auth/jwt.ts +39 -0
  213. package/src/lib/auth/server.ts +199 -0
  214. package/src/lib/boolean.ts +17 -0
  215. package/src/lib/bootstrap/appResolver.ts +85 -0
  216. package/src/lib/bootstrap/dynamicLoader.ts +177 -0
  217. package/src/lib/bootstrap/factory.ts +108 -0
  218. package/src/lib/bootstrap/index.ts +23 -0
  219. package/src/lib/bootstrap/types.ts +31 -0
  220. package/src/lib/cache/segments.ts +56 -0
  221. package/src/lib/cli/progress.ts +55 -0
  222. package/src/lib/commands/__tests__/command-bus.test.ts +84 -0
  223. package/src/lib/commands/__tests__/helpers.test.ts +42 -0
  224. package/src/lib/commands/command-bus.ts +349 -0
  225. package/src/lib/commands/customFieldSnapshots.ts +86 -0
  226. package/src/lib/commands/helpers.ts +143 -0
  227. package/src/lib/commands/index.ts +4 -0
  228. package/src/lib/commands/operationMetadata.ts +40 -0
  229. package/src/lib/commands/registry.ts +46 -0
  230. package/src/lib/commands/scope.ts +59 -0
  231. package/src/lib/commands/types.ts +63 -0
  232. package/src/lib/crud/__tests__/crud-factory.test.ts +333 -0
  233. package/src/lib/crud/__tests__/custom-fields.test.ts +150 -0
  234. package/src/lib/crud/cache-stats.ts +127 -0
  235. package/src/lib/crud/cache.ts +205 -0
  236. package/src/lib/crud/custom-fields-client.ts +54 -0
  237. package/src/lib/crud/custom-fields.ts +607 -0
  238. package/src/lib/crud/errors.ts +23 -0
  239. package/src/lib/crud/exporters.ts +188 -0
  240. package/src/lib/crud/factory.ts +1622 -0
  241. package/src/lib/crud/types.ts +29 -0
  242. package/src/lib/custom-fields/normalize.ts +45 -0
  243. package/src/lib/data/engine.ts +562 -0
  244. package/src/lib/db/escapeLikePattern.ts +2 -0
  245. package/src/lib/db/mikro.ts +100 -0
  246. package/src/lib/di/container.ts +105 -0
  247. package/src/lib/email/send.ts +18 -0
  248. package/src/lib/encryption/__tests__/customFieldValues.test.ts +63 -0
  249. package/src/lib/encryption/__tests__/indexDoc.test.ts +115 -0
  250. package/src/lib/encryption/aes.ts +64 -0
  251. package/src/lib/encryption/customFieldValues.ts +67 -0
  252. package/src/lib/encryption/entityFields.ts +39 -0
  253. package/src/lib/encryption/entityIds.ts +107 -0
  254. package/src/lib/encryption/find.ts +81 -0
  255. package/src/lib/encryption/indexDoc.ts +104 -0
  256. package/src/lib/encryption/kms.ts +337 -0
  257. package/src/lib/encryption/subscriber.ts +416 -0
  258. package/src/lib/encryption/tenantDataEncryptionService.ts +313 -0
  259. package/src/lib/encryption/toggles.ts +15 -0
  260. package/src/lib/entities/naming.ts +6 -0
  261. package/src/lib/entities/system-entities.ts +43 -0
  262. package/src/lib/frontend/organizationEvents.ts +55 -0
  263. package/src/lib/frontend/useOrganizationScope.ts +30 -0
  264. package/src/lib/hotkeys/index.ts +168 -0
  265. package/src/lib/i18n/app-dictionaries.ts +18 -0
  266. package/src/lib/i18n/config.ts +4 -0
  267. package/src/lib/i18n/context.tsx +66 -0
  268. package/src/lib/i18n/server.ts +74 -0
  269. package/src/lib/i18n/translate.ts +54 -0
  270. package/src/lib/indexers/error-log.ts +106 -0
  271. package/src/lib/indexers/status-log.ts +119 -0
  272. package/src/lib/lib/auth/jwt.ts +39 -0
  273. package/src/lib/lib/auth/server.ts +94 -0
  274. package/src/lib/lib/email/send.ts +18 -0
  275. package/src/lib/lib/i18n/config.ts +4 -0
  276. package/src/lib/lib/i18n/context.tsx +38 -0
  277. package/src/lib/lib/utils.ts +6 -0
  278. package/src/lib/location/countries.ts +97 -0
  279. package/src/lib/modules/index.ts +1 -0
  280. package/src/lib/modules/registry.ts +18 -0
  281. package/src/lib/openapi/crud.ts +218 -0
  282. package/src/lib/openapi/generator.ts +1311 -0
  283. package/src/lib/openapi/index.ts +4 -0
  284. package/src/lib/openapi/sanitize.ts +137 -0
  285. package/src/lib/openapi/types.ts +79 -0
  286. package/src/lib/profiler/index.ts +371 -0
  287. package/src/lib/query/__tests__/engine.test.ts +274 -0
  288. package/src/lib/query/engine.ts +837 -0
  289. package/src/lib/query/join-utils.ts +238 -0
  290. package/src/lib/query/types.ts +121 -0
  291. package/src/lib/search/config.ts +49 -0
  292. package/src/lib/search/tokenize.ts +45 -0
  293. package/src/lib/slugify.ts +28 -0
  294. package/src/lib/testing/bootstrap.ts +124 -0
  295. package/src/lib/testing/index.ts +15 -0
  296. package/src/lib/testing/renderWithProviders.tsx +31 -0
  297. package/src/lib/url.ts +12 -0
  298. package/src/lib/utils.ts +17 -0
  299. package/src/lib/version.ts +5 -0
  300. package/src/modules/__tests__/dsl.test.ts +35 -0
  301. package/src/modules/__tests__/registry.test.ts +300 -0
  302. package/src/modules/dashboard/widgets.ts +57 -0
  303. package/src/modules/dsl.ts +32 -0
  304. package/src/modules/entities/__tests__/validation.test.ts +52 -0
  305. package/src/modules/entities/kinds.ts +20 -0
  306. package/src/modules/entities/options.ts +36 -0
  307. package/src/modules/entities/validation.ts +118 -0
  308. package/src/modules/entities/validators.ts +93 -0
  309. package/src/modules/entities.ts +102 -0
  310. package/src/modules/navigation/sidebarPreferences.ts +62 -0
  311. package/src/modules/perspectives/types.ts +40 -0
  312. package/src/modules/registry.ts +249 -0
  313. package/src/modules/search.ts +325 -0
  314. package/src/modules/vector.ts +122 -0
  315. package/src/modules/widgets/__tests__/injection.test.ts +48 -0
  316. package/src/modules/widgets/injection-loader.ts +235 -0
  317. package/src/modules/widgets/injection.ts +120 -0
  318. package/src/security/features.ts +22 -0
  319. package/src/types/pg.d.ts +2 -0
  320. package/src/types/react-email.d.ts +2 -0
  321. package/src/types/resend.d.ts +2 -0
  322. package/tsconfig.build.json +11 -0
  323. package/tsconfig.json +9 -0
  324. package/watch.mjs +6 -0
@@ -0,0 +1,274 @@
1
+ import { BasicQueryEngine } from '../engine'
2
+ import { SortDir } from '../types'
3
+ import { registerModules } from '../../i18n/server'
4
+
5
+ // Mock modules with one entity extension
6
+ const mockModules = [
7
+ { id: 'auth', entityExtensions: [ { base: 'auth:user', extension: 'my_module:user_profile', join: { baseKey: 'id', extensionKey: 'user_id' } } ] },
8
+ ]
9
+
10
+ // Register modules for the registration-based pattern
11
+ registerModules(mockModules as any)
12
+
13
+ type FakeData = Record<string, any[]>
14
+
15
+ function cloneRows(rows: any[] | undefined): any[] {
16
+ if (!rows) return []
17
+ return rows.map((row) => ({ ...row }))
18
+ }
19
+
20
+ function createFakeKnex(overrides?: FakeData) {
21
+ const calls: any[] = []
22
+ const defaultData: FakeData = {
23
+ custom_field_defs: [
24
+ { key: 'vip', entity_id: 'auth:user', is_active: true, config_json: '{}', kind: 'boolean' },
25
+ { key: 'industry', entity_id: 'auth:user', is_active: true, config_json: '{}', kind: 'select' },
26
+ ],
27
+ custom_field_values: [],
28
+ }
29
+ const sourceData = { ...defaultData, ...(overrides || {}) }
30
+ const data: FakeData = Object.fromEntries(
31
+ Object.entries(sourceData).map(([table, rows]) => [table, cloneRows(rows)])
32
+ )
33
+ function raw(sql: string, params?: any[]) { return { toString: () => sql, sql, params } }
34
+ function builderFor(tableArg: any) {
35
+ let table = ''
36
+ let alias: string | null = null
37
+ if (typeof tableArg === 'string') {
38
+ table = tableArg
39
+ } else if (tableArg && typeof tableArg === 'object') {
40
+ const first = Object.entries(tableArg)[0]
41
+ if (first) {
42
+ alias = String(first[0])
43
+ table = String(first[1])
44
+ }
45
+ } else {
46
+ table = String(tableArg || '')
47
+ }
48
+ const ops = { table, alias, wheres: [] as any[], joins: [] as any[], selects: [] as any[], orderBys: [] as any[], groups: [] as any[], limits: 0, offsets: 0, isCountDistinct: false }
49
+ function recordJoin(type: 'left' | 'inner', aliasObj: any, fn: Function, ctxBuilder: () => any) {
50
+ const entry: any = { type, aliasObj, conditions: [] as any[] }
51
+ const ctx = ctxBuilder()
52
+ ctx.on = (left: any, op: any, right: any) => {
53
+ entry.conditions.push({ method: 'on', args: [left, op, right] })
54
+ return ctx
55
+ }
56
+ ctx.andOn = (left: any, op: any, right: any) => {
57
+ entry.conditions.push({ method: 'andOn', args: [left, op, right] })
58
+ return ctx
59
+ }
60
+ fn.call(ctx)
61
+ ops.joins.push(entry)
62
+ }
63
+ const b: any = {
64
+ _ops: ops,
65
+ select: function (...cols: any[]) { ops.selects.push(cols); return this },
66
+ where: function (...args: any[]) { ops.wheres.push(args); return this },
67
+ andWhere: function (...args: any[]) { ops.wheres.push(args); return this },
68
+ whereIn: function (...args: any[]) { ops.wheres.push(['in', ...args]); return this },
69
+ whereNotIn: function (...args: any[]) { ops.wheres.push(['notIn', ...args]); return this },
70
+ whereNull: function (col: any) { ops.wheres.push(['isNull', col]); return this },
71
+ whereNotNull: function (col: any) { ops.wheres.push(['notNull', col]); return this },
72
+ whereExists: function (sub: any) { ops.wheres.push(['exists', sub]); return this },
73
+ whereNotExists: function (sub: any) { ops.wheres.push(['notExists', sub]); return this },
74
+ whereRaw: function (...args: any[]) { ops.wheres.push(['raw', ...args]); return this },
75
+ orWhereNull: function (col: any) { ops.wheres.push(['orWhereNull', col]); return this },
76
+ leftJoin: function (aliasObj: any, fn: Function) {
77
+ recordJoin('left', aliasObj, fn, () => ({}))
78
+ return this
79
+ },
80
+ join: function (aliasObj: any, fn: Function) {
81
+ recordJoin('inner', aliasObj, fn, () => ({}))
82
+ return this
83
+ },
84
+ orderBy: function (col: any, dir?: any) { ops.orderBys.push([col, dir]); return this },
85
+ groupBy: function (col: any) { ops.groups.push(col); return this },
86
+ limit: function (n: number) { ops.limits = n; return this },
87
+ offset: function (n: number) { ops.offsets = n; return this },
88
+ clone: function () { return this },
89
+ countDistinct: function () { ops.isCountDistinct = true; return this },
90
+ count: async function () { return [{ count: '0' }] },
91
+ first: async function () {
92
+ // If this is called after countDistinct, return count data
93
+ if (ops.isCountDistinct) {
94
+ return { count: '0' }
95
+ }
96
+ const rows = data[table] || [];
97
+ return rows[0]
98
+ },
99
+ modify: function (fn: Function) {
100
+ const qb: any = {
101
+ andWhere: (arg: any) => {
102
+ if (typeof arg === 'function') {
103
+ const inner: any = {
104
+ where: (obj: any) => ({
105
+ orWhereNull: (col: any) => { ops.wheres.push(['andWhereFn', obj, ['orWhereNull', col]]); return inner },
106
+ }),
107
+ }
108
+ arg(inner)
109
+ } else {
110
+ ops.wheres.push(['andWhere', arg])
111
+ }
112
+ return qb
113
+ },
114
+ whereNull: (col: any) => { ops.wheres.push(['isNull', col]); return qb },
115
+ }
116
+ fn(qb)
117
+ return this
118
+ },
119
+ then: function (resolve: any) { const res = data[table] || []; return Promise.resolve(resolve(res)) },
120
+ }
121
+ calls.push(b)
122
+ return b
123
+ }
124
+ const fn: any = (table: any) => builderFor(table)
125
+ fn.raw = raw
126
+ fn._calls = calls
127
+ return fn
128
+ }
129
+
130
+ describe('BasicQueryEngine', () => {
131
+ test('pluralizes entity names ending with y correctly', async () => {
132
+ const fakeKnex = createFakeKnex()
133
+ const engine = new BasicQueryEngine({} as any, () => fakeKnex as any)
134
+ await engine.query('customers:customer_entity', { tenantId: 't1' })
135
+ const baseCall = fakeKnex._calls.find((b: any) => b._ops.table === 'customer_entities')
136
+ expect(baseCall).toBeTruthy()
137
+ })
138
+
139
+ test('includeCustomFields true discovers keys and allows sort on cf:*; joins extensions', async () => {
140
+ const fakeKnex = createFakeKnex()
141
+ const engine = new BasicQueryEngine({} as any, () => fakeKnex as any)
142
+ const res = await engine.query('auth:user', {
143
+ includeCustomFields: true,
144
+ fields: ['id','email','cf:vip'],
145
+ sort: [{ field: 'cf:vip', dir: SortDir.Asc }],
146
+ includeExtensions: true,
147
+ organizationId: '1',
148
+ tenantId: 't1',
149
+ page: { page: 1, pageSize: 10 },
150
+ })
151
+ expect(res).toMatchObject({ page: 1, pageSize: 10, total: 0, items: [] })
152
+ // Assert that custom_field_defs was queried for this entity and org
153
+ const defsCall = fakeKnex._calls.find((b: any) => b._ops.table === 'custom_field_defs')
154
+ expect(defsCall).toBeTruthy()
155
+ const hasEntityFilter = defsCall._ops.wheres.some((w: any) => JSON.stringify(w).includes('entity_id'))
156
+ expect(hasEntityFilter).toBe(true)
157
+ // Organization-level scoping is intentionally disabled for custom field definitions; ensure tenant filter is present
158
+ const hasTenantFilter = defsCall._ops.wheres.some((w: any) => JSON.stringify(w).includes('tenant_id'))
159
+ expect(hasTenantFilter).toBe(true)
160
+ // Assert base ordering by cf alias was recorded
161
+ const baseCall = fakeKnex._calls.find((b: any) => b._ops.table === 'users')
162
+ const hasCfOrder = baseCall._ops.orderBys.some((o: any) => o[0] === 'cf_vip')
163
+ expect(hasCfOrder).toBe(true)
164
+ // Assert an extension leftJoin was attempted
165
+ const hasExtJoin = baseCall._ops.joins.length > 0
166
+ expect(hasExtJoin).toBe(true)
167
+ })
168
+
169
+ test('customFieldSources join additional profiles for custom fields', async () => {
170
+ const fakeKnex = createFakeKnex({
171
+ custom_field_defs: [
172
+ { key: 'birthday', entity_id: 'customers:customer_person_profile', is_active: true, config_json: JSON.stringify({ listVisible: true }), kind: 'text' },
173
+ { key: 'sector', entity_id: 'customers:customer_company_profile', is_active: true, config_json: JSON.stringify({ listVisible: true }), kind: 'select' },
174
+ ],
175
+ custom_field_values: [],
176
+ customer_entities: [],
177
+ customer_people: [],
178
+ customer_companies: [],
179
+ })
180
+ const engine = new BasicQueryEngine({} as any, () => fakeKnex as any)
181
+ await engine.query('customers:customer_entity', {
182
+ tenantId: 't1',
183
+ includeCustomFields: ['birthday', 'sector'],
184
+ fields: ['id', 'cf:birthday', 'cf:sector'],
185
+ customFieldSources: [
186
+ {
187
+ entityId: 'customers:customer_person_profile',
188
+ table: 'customer_people',
189
+ alias: 'person_profile',
190
+ recordIdColumn: 'id',
191
+ join: { fromField: 'id', toField: 'entity_id' },
192
+ },
193
+ {
194
+ entityId: 'customers:customer_company_profile',
195
+ table: 'customer_companies',
196
+ alias: 'company_profile',
197
+ recordIdColumn: 'id',
198
+ join: { fromField: 'id', toField: 'entity_id' },
199
+ },
200
+ ],
201
+ page: { page: 1, pageSize: 10 },
202
+ })
203
+ const baseCall = fakeKnex._calls.find((b: any) => b._ops.table === 'customer_entities')
204
+ expect(baseCall).toBeTruthy()
205
+ const joinAliases = baseCall._ops.joins.map((j: any) => Object.keys(j.aliasObj)[0])
206
+ expect(joinAliases).toEqual(expect.arrayContaining([
207
+ 'person_profile',
208
+ 'company_profile',
209
+ 'cfd_person_profile_birthday',
210
+ 'cfv_person_profile_birthday',
211
+ 'cfd_company_profile_sector',
212
+ 'cfv_company_profile_sector',
213
+ ]))
214
+ const personProfileJoin = baseCall._ops.joins.find((j: any) => j.aliasObj.person_profile)
215
+ expect(personProfileJoin?.conditions.some((c: any) => c.args[0] === 'person_profile.entity_id' && c.args[2] === 'customer_entities.id')).toBe(true)
216
+ const companyProfileJoin = baseCall._ops.joins.find((j: any) => j.aliasObj.company_profile)
217
+ expect(companyProfileJoin?.conditions.some((c: any) => c.args[0] === 'company_profile.entity_id' && c.args[2] === 'customer_entities.id')).toBe(true)
218
+ const cfvPersonJoin = baseCall._ops.joins.find((j: any) => j.aliasObj.cfv_person_profile_birthday)
219
+ expect(cfvPersonJoin?.conditions.some((c: any) => c.args[0] === 'cfv_person_profile_birthday.record_id' && c.args[2]?.params?.[0] === 'person_profile.id')).toBe(true)
220
+ const cfvCompanyJoin = baseCall._ops.joins.find((j: any) => j.aliasObj.cfv_company_profile_sector)
221
+ expect(cfvCompanyJoin?.conditions.some((c: any) => c.args[0] === 'cfv_company_profile_sector.record_id' && c.args[2]?.params?.[0] === 'company_profile.id')).toBe(true)
222
+ const defsEntityWhere = fakeKnex._calls
223
+ .filter((b: any) => b._ops.table === 'custom_field_defs')
224
+ .flatMap((b: any) => b._ops.wheres)
225
+ .find((w: any) => Array.isArray(w) && w[0] === 'in' && w[1] === 'entity_id')
226
+ expect(defsEntityWhere).toBeTruthy()
227
+ const entityTargets = defsEntityWhere?.[2] || []
228
+ expect(entityTargets).toEqual(expect.arrayContaining([
229
+ 'customers:customer_entity',
230
+ 'customers:customer_person_profile',
231
+ 'customers:customer_company_profile',
232
+ ]))
233
+ })
234
+
235
+ test('join filters use whereExists with configured alias', async () => {
236
+ const fakeKnex = createFakeKnex({
237
+ customer_entities: [],
238
+ customer_tag_assignments: [],
239
+ 'information_schema.columns': [
240
+ { table_name: 'customer_tag_assignments', column_name: 'tag_id' },
241
+ { table_name: 'customer_tag_assignments', column_name: 'tenant_id' },
242
+ { table_name: 'customer_entities', column_name: 'tenant_id' },
243
+ ],
244
+ })
245
+ const engine = new BasicQueryEngine({} as any, () => fakeKnex as any)
246
+ await engine.query('customers:customer_entity', {
247
+ tenantId: 't1',
248
+ fields: ['id'],
249
+ joins: [
250
+ {
251
+ alias: 'tag_assignments',
252
+ table: 'customer_tag_assignments',
253
+ from: { field: 'id' },
254
+ to: { field: 'entity_id' },
255
+ type: 'left',
256
+ },
257
+ ],
258
+ filters: {
259
+ 'tag_assignments.tag_id': { $in: ['tag-1', 'tag-2'] },
260
+ },
261
+ page: { page: 1, pageSize: 10 },
262
+ })
263
+ const baseCall = fakeKnex._calls.find((b: any) => b._ops.table === 'customer_entities')
264
+ expect(baseCall).toBeTruthy()
265
+ const existsFilter = baseCall._ops.wheres.find((w: any) => Array.isArray(w) && w[0] === 'exists')
266
+ expect(existsFilter).toBeTruthy()
267
+ const subQuery = existsFilter[1]
268
+ expect(subQuery?._ops?.table).toBe('customer_tag_assignments')
269
+ const hasInFilter = Array.isArray(subQuery?._ops?.wheres)
270
+ ? subQuery._ops.wheres.some((w: any) => Array.isArray(w) && w[0] === 'in' && w[1] === 'tag_assignments.tag_id')
271
+ : false
272
+ expect(hasInFilter).toBe(true)
273
+ })
274
+ })