@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,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/query/engine.ts"],
4
+ "sourcesContent": ["import type { QueryEngine, QueryOptions, QueryResult, QueryCustomFieldSource } from './types'\nimport type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Knex } from 'knex'\nimport {\n applyJoinFilters,\n normalizeFilters,\n partitionFilters,\n resolveJoins,\n type BaseFilter,\n type NormalizedFilter,\n type ResolvedJoin,\n} from './join-utils'\nimport { resolveSearchConfig } from '../search/config'\nimport { tokenizeText } from '../search/tokenize'\n\nconst entityTableCache = new Map<string, string>()\n\ntype EncryptionResolver = () => {\n decryptEntityPayload?: (entityId: EntityId, payload: Record<string, unknown>, tenantId?: string | null, organizationId?: string | null) => Promise<Record<string, unknown>>\n isEnabled?: () => boolean\n} | null\n\ntype ResolvedCustomFieldSource = {\n entityId: EntityId\n alias: string\n table: string\n recordIdExpr: any\n}\n\ntype ResultRow = Record<string, unknown>\n\nconst pluralizeBaseName = (name: string): string => {\n if (!name) return name\n if (name.endsWith('s')) return name\n if (name.endsWith('y')) return `${name.slice(0, -1)}ies`\n return `${name}s`\n}\n\nconst toPascalCase = (value: string): string => {\n return value\n .split(/[_\\s]+/)\n .filter(Boolean)\n .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))\n .join('')\n}\n\nconst candidateClassNames = (rawName: string): string[] => {\n const base = toPascalCase(rawName)\n const candidates = new Set<string>()\n if (base) candidates.add(base)\n if (base && !base.endsWith('Entity')) candidates.add(`${base}Entity`)\n return Array.from(candidates)\n}\n\nexport function resolveEntityTableName(em: EntityManager | undefined, entity: EntityId): string {\n if (entityTableCache.has(entity)) {\n return entityTableCache.get(entity)!\n }\n const parts = String(entity || '').split(':')\n const rawName = (parts[1] && parts[1].trim().length > 0) ? parts[1] : (parts[0] || '').trim()\n const metadata = (em as any)?.getMetadata?.()\n\n if (metadata && rawName) {\n const candidates = candidateClassNames(rawName)\n for (const candidate of candidates) {\n try {\n const meta = metadata.find?.(candidate)\n if (meta?.tableName) {\n const tableName = String(meta.tableName)\n entityTableCache.set(entity, tableName)\n return tableName\n }\n } catch {}\n }\n }\n\n const fallback = pluralizeBaseName(rawName || '')\n entityTableCache.set(entity, fallback)\n return fallback\n}\n\n\n// Minimal default implementation placeholder.\n// For now, only supports basic base-entity querying by table name inferred from EntityId ('<module>:<entity>' -> '<entities>') via convention.\n// Extensions and custom fields will be added iteratively.\n\nexport class BasicQueryEngine implements QueryEngine {\n private columnCache = new Map<string, boolean>()\n private tableCache = new Map<string, boolean>()\n private searchAliasSeq = 0\n\n constructor(\n private em: EntityManager,\n private getKnexFn?: () => any,\n private resolveEncryptionService?: EncryptionResolver,\n ) {}\n\n private getEncryptionService() {\n try {\n return this.resolveEncryptionService?.() ?? null\n } catch {\n return null\n }\n }\n\n async query<T = any>(entity: EntityId, opts: QueryOptions = {}): Promise<QueryResult<T>> {\n // Heuristic: map '<module>:user' -> table 'users'\n const table = resolveEntityTableName(this.em, entity)\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n\n let q = knex(table)\n const qualify = (col: string) => `${table}.${col}`\n const orgScope = this.resolveOrganizationScope(opts)\n this.searchAliasSeq = 0\n // Require tenant scope for all queries\n if (!opts.tenantId) {\n throw new Error(\n 'QueryEngine: tenantId is now required for all queries (breaking change). ' +\n 'Please provide a tenantId in QueryOptions, e.g., query(entity, { tenantId: ... }). ' +\n 'See migration guide or documentation for details.'\n )\n }\n // Optional organization filter (when present in schema)\n if (orgScope && await this.columnExists(table, 'organization_id')) {\n q = this.applyOrganizationScope(q, qualify('organization_id'), orgScope)\n }\n // Tenant guard (required) when present in schema\n if (await this.columnExists(table, 'tenant_id')) {\n q = q.where(qualify('tenant_id'), opts.tenantId)\n }\n // Default soft-delete guard: exclude rows with deleted_at when column exists\n if (!opts.withDeleted && await this.columnExists(table, 'deleted_at')) {\n q = q.whereNull(qualify('deleted_at'))\n }\n\n const normalizedFilters = normalizeFilters(opts.filters)\n const resolvedJoins = resolveJoins(table, opts.joins, (entityId) => resolveEntityTableName(this.em, entityId as any))\n const joinMap = new Map<string, ResolvedJoin>()\n const aliasTables = new Map<string, string>()\n aliasTables.set(table, table)\n aliasTables.set('base', table)\n for (const join of resolvedJoins) {\n joinMap.set(join.alias, join)\n aliasTables.set(join.alias, join.table)\n }\n const { baseFilters, joinFilters } = partitionFilters(table, normalizedFilters, joinMap)\n const cfFilters = normalizedFilters.filter((filter) => String(filter.field).startsWith('cf:'))\n const searchConfig = resolveSearchConfig()\n const searchEnabled = searchConfig.enabled && await this.tableExists('search_tokens')\n const hasSearchTokens = searchEnabled\n ? await this.hasSearchTokens(String(entity), opts.tenantId ?? null, orgScope)\n : false\n const searchActive = searchEnabled && hasSearchTokens\n const searchFilters = [...baseFilters, ...cfFilters].filter((filter) => filter.op === 'like' || filter.op === 'ilike')\n if (searchFilters.length) {\n const fields = searchFilters.map((filter) => String(filter.field))\n this.logSearchDebug('search:init', {\n entity: String(entity),\n table,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n fields,\n searchEnabled,\n hasSearchTokens,\n searchActive,\n searchConfig: {\n enabled: searchConfig.enabled,\n minTokenLength: searchConfig.minTokenLength,\n enablePartials: searchConfig.enablePartials,\n hashAlgorithm: searchConfig.hashAlgorithm,\n blocklistedFields: searchConfig.blocklistedFields,\n },\n })\n if (!searchEnabled) {\n this.logSearchDebug('search:disabled', { entity: String(entity), table })\n } else if (!hasSearchTokens) {\n this.logSearchDebug('search:no-search-tokens', {\n entity: String(entity),\n table,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n }\n }\n const recordIdColumn = qualify('id')\n\n const applyFilterOp = (builder: any, column: string, op: any, value: any, fieldName?: string) => {\n if (\n (op === 'like' || op === 'ilike') &&\n searchActive &&\n typeof value === 'string' &&\n fieldName\n ) {\n const tokens = tokenizeText(String(value), searchConfig)\n const hashes = tokens.hashes\n if (hashes.length) {\n const applied = this.applySearchTokens(builder, {\n entity: String(entity),\n field: fieldName,\n hashes,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n tokens: tokens.tokens,\n })\n this.logSearchDebug('search:filter', {\n entity: String(entity),\n field: fieldName,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n if (applied) return builder\n } else {\n this.logSearchDebug('search:skip-empty-hashes', {\n entity: String(entity),\n field: fieldName,\n value,\n })\n }\n }\n switch (op) {\n case 'eq': builder.where(column, value); break\n case 'ne': builder.whereNot(column, value); break\n case 'gt': builder.where(column, '>', value); break\n case 'gte': builder.where(column, '>=', value); break\n case 'lt': builder.where(column, '<', value); break\n case 'lte': builder.where(column, '<=', value); break\n case 'in': builder.whereIn(column, Array.isArray(value) ? value : [value]); break\n case 'nin': builder.whereNotIn(column, Array.isArray(value) ? value : [value]); break\n case 'like': builder.where(column, 'like', value); break\n case 'ilike': builder.where(column, 'ilike', value); break\n case 'exists': value ? builder.whereNotNull(column) : builder.whereNull(column); break\n default: break\n }\n return builder\n }\n\n for (const filter of baseFilters) {\n let qualified = filter.qualified ?? null\n if (!qualified) {\n const column = await this.resolveBaseColumn(table, String(filter.field))\n if (!column) continue\n qualified = qualify(column)\n }\n applyFilterOp(q, qualified, filter.op, filter.value, String(filter.field))\n }\n\n const applyAliasScopes = async (builder: any, aliasName: string) => {\n const targetTable = aliasTables.get(aliasName)\n if (!targetTable) return\n if (orgScope && await this.columnExists(targetTable, 'organization_id')) {\n this.applyOrganizationScope(builder, `${aliasName}.organization_id`, orgScope)\n }\n if (opts.tenantId && await this.columnExists(targetTable, 'tenant_id')) {\n builder.where(`${aliasName}.tenant_id`, opts.tenantId)\n }\n }\n await applyJoinFilters({\n knex,\n baseTable: table,\n builder: q,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase: (column) => qualify(column),\n applyAliasScope: (builder, alias) => applyAliasScopes(builder, alias),\n applyFilterOp,\n columnExists: (tbl, column) => this.columnExists(tbl, column),\n })\n // Selection (base columns only here; cf:* handled later)\n if (opts.fields && opts.fields.length) {\n const cols = opts.fields.filter((f) => !f.startsWith('cf:'))\n if (cols.length) {\n // Qualify and alias to base names to avoid ambiguity\n const baseSelects = cols.map((c) => knex.raw('?? as ??', [qualify(c), c]))\n q = q.select(baseSelects)\n }\n } else {\n // Default to selecting only base table columns to avoid ambiguity when joining\n q = q.select(knex.raw('??.*', [table]))\n }\n\n // Resolve which custom fields to include\n const tenantId = opts.tenantId\n const sanitize = (s: string) => s.replace(/[^a-zA-Z0-9_]/g, '_')\n const cfSources = this.configureCustomFieldSources(q, table, entity, knex, opts, qualify)\n const entityIdToSource = new Map<string, ResolvedCustomFieldSource>()\n for (const source of cfSources) {\n entityIdToSource.set(String(source.entityId), source)\n }\n const requestedCustomFieldKeys = Array.isArray(opts.includeCustomFields)\n ? opts.includeCustomFields.map((key) => String(key))\n : []\n const cfKeys = new Set<string>()\n const keySource = new Map<string, ResolvedCustomFieldSource>()\n // Explicit in fields/filters\n for (const f of (opts.fields || [])) {\n if (typeof f === 'string' && f.startsWith('cf:')) cfKeys.add(f.slice(3))\n }\n for (const f of cfFilters) {\n if (typeof f.field === 'string' && f.field.startsWith('cf:')) cfKeys.add(f.field.slice(3))\n }\n if (opts.includeCustomFields === true) {\n if (entityIdToSource.size > 0) {\n const entityIdList = Array.from(entityIdToSource.keys())\n const entityOrder = new Map<string, number>()\n entityIdList.forEach((id, idx) => entityOrder.set(id, idx))\n const rows = await knex('custom_field_defs')\n .select('key', 'entity_id', 'config_json', 'kind')\n .whereIn('entity_id', entityIdList)\n .andWhere('is_active', true)\n .modify((qb: any) => {\n qb.andWhere((inner: any) => {\n inner.where({ tenant_id: tenantId }).orWhereNull('tenant_id')\n })\n })\n type CustomFieldDefinitionRow = {\n key: string\n entityId: string\n kind: string\n config: Record<string, unknown>\n }\n const sorted: CustomFieldDefinitionRow[] = rows.map((row: any) => {\n const raw = row.config_json\n let cfg: Record<string, any> = {}\n if (raw && typeof raw === 'string') {\n try { cfg = JSON.parse(raw) } catch { cfg = {} }\n } else if (raw && typeof raw === 'object') {\n cfg = raw\n }\n return {\n key: String(row.key),\n entityId: String(row.entity_id),\n kind: String(row.kind || ''),\n config: cfg,\n }\n })\n sorted.sort((a: CustomFieldDefinitionRow, b: CustomFieldDefinitionRow) => {\n const ai = entityOrder.get(a.entityId) ?? Number.MAX_SAFE_INTEGER\n const bi = entityOrder.get(b.entityId) ?? Number.MAX_SAFE_INTEGER\n if (ai !== bi) return ai - bi\n return a.key.localeCompare(b.key)\n })\n const selectedSources = new Map<string, { source: ResolvedCustomFieldSource; score: number; penalty: number; entityIndex: number }>()\n for (const row of sorted) {\n const source = entityIdToSource.get(row.entityId)\n if (!source) continue\n const cfg = row.config || {}\n const entityIndex = entityOrder.get(row.entityId) ?? Number.MAX_SAFE_INTEGER\n const scores = computeScore(cfg, row.kind, entityIndex)\n const existing = selectedSources.get(row.key)\n if (!existing || scores.base > existing.score || (scores.base === existing.score && (scores.penalty < existing.penalty || (scores.penalty === existing.penalty && scores.entityIndex < existing.entityIndex)))) {\n selectedSources.set(row.key, { source, score: scores.base, penalty: scores.penalty, entityIndex: scores.entityIndex })\n }\n cfKeys.add(row.key)\n }\n for (const [key, entry] of selectedSources.entries()) {\n keySource.set(key, entry.source)\n }\n }\n } else if (requestedCustomFieldKeys.length > 0) {\n for (const key of requestedCustomFieldKeys) cfKeys.add(key)\n }\n const unresolvedKeys = Array.from(cfKeys).filter((key) => !keySource.has(key))\n if (unresolvedKeys.length > 0 && entityIdToSource.size > 0) {\n const rows = await knex('custom_field_defs')\n .select('key', 'entity_id')\n .whereIn('entity_id', Array.from(entityIdToSource.keys()))\n .whereIn('key', unresolvedKeys)\n .andWhere('is_active', true)\n .modify((qb: any) => {\n qb.andWhere((inner: any) => {\n inner.where({ tenant_id: tenantId }).orWhereNull('tenant_id')\n })\n })\n for (const row of rows) {\n const source = entityIdToSource.get(String(row.entity_id))\n if (!source) continue\n if (!keySource.has(row.key)) keySource.set(row.key, source)\n }\n }\n\n const cfValueExprByKey: Record<string, any> = {}\n const cfSelectedAliases: string[] = []\n const cfJsonAliases = new Set<string>()\n const cfMultiAliasByAlias = new Map<string, string>()\n for (const key of cfKeys) {\n const source = keySource.get(key)\n if (!source) continue\n const entityIdForKey = source.entityId\n const recordIdExpr = source.recordIdExpr\n const sourceAliasSafe = sanitize(source.alias || 'src')\n const keyAliasSafe = sanitize(key)\n const defAlias = `cfd_${sourceAliasSafe}_${keyAliasSafe}`\n const valAlias = `cfv_${sourceAliasSafe}_${keyAliasSafe}`\n // Join definitions for kind resolution\n q = q.leftJoin({ [defAlias]: 'custom_field_defs' }, function (this: any) {\n this.on(`${defAlias}.entity_id`, '=', knex.raw('?', [entityIdForKey]))\n .andOn(`${defAlias}.key`, '=', knex.raw('?', [key]))\n .andOn(`${defAlias}.is_active`, '=', knex.raw('true'))\n .andOn(knex.raw(`(${defAlias}.tenant_id = ? OR ${defAlias}.tenant_id IS NULL)`, [tenantId]))\n })\n // Join values with record match\n q = q.leftJoin({ [valAlias]: 'custom_field_values' }, function (this: any) {\n this.on(`${valAlias}.entity_id`, '=', knex.raw('?', [entityIdForKey]))\n .andOn(`${valAlias}.field_key`, '=', knex.raw('?', [key]))\n .andOn(`${valAlias}.record_id`, '=', recordIdExpr)\n .andOn(knex.raw(`(${valAlias}.tenant_id = ? OR ${valAlias}.tenant_id IS NULL)`, [tenantId]))\n })\n // Force a common SQL type across branches to avoid Postgres CASE type conflicts\n const caseExpr = knex.raw(\n `CASE ${defAlias}.kind\n WHEN 'integer' THEN (${valAlias}.value_int)::text\n WHEN 'float' THEN (${valAlias}.value_float)::text\n WHEN 'boolean' THEN (${valAlias}.value_bool)::text\n WHEN 'multiline' THEN (${valAlias}.value_multiline)::text\n ELSE (${valAlias}.value_text)::text\n END`\n )\n cfValueExprByKey[key] = caseExpr\n const alias = sanitize(`cf:${key}`)\n // Project as aggregated to avoid duplicates when multi values exist\n if ((opts.fields || []).includes(`cf:${key}`) || opts.includeCustomFields === true || (requestedCustomFieldKeys.length > 0 && requestedCustomFieldKeys.includes(key))) {\n // Use bool_or over config_json->>multi so it's valid under GROUP BY\n const isMulti = knex.raw(`bool_or(coalesce((${defAlias}.config_json->>'multi')::boolean, false))`)\n const aggregatedArray = `array_remove(array_agg(DISTINCT ${caseExpr.toString()}), NULL)`\n const expr = `CASE WHEN ${isMulti.toString()}\n THEN to_jsonb(${aggregatedArray})\n ELSE to_jsonb(max(${caseExpr.toString()}))\n END`\n const multiAlias = `${alias}__is_multi`\n q = q.select(knex.raw(`${expr} as ??`, [alias]))\n q = q.select(knex.raw(`${isMulti.toString()} as ??`, [multiAlias]))\n cfSelectedAliases.push(alias)\n cfJsonAliases.add(alias)\n cfMultiAliasByAlias.set(alias, multiAlias)\n }\n }\n\n // Apply cf:* filters (on raw expressions)\n for (const f of cfFilters) {\n if (!f.field.startsWith('cf:')) continue\n const key = f.field.slice(3)\n const expr = cfValueExprByKey[key]\n if (!expr) continue\n if ((f.op === 'like' || f.op === 'ilike') && searchActive && typeof f.value === 'string') {\n const tokens = tokenizeText(String(f.value), searchConfig)\n const hashes = tokens.hashes\n if (hashes.length) {\n const applied = this.applySearchTokens(q, {\n entity: String(entity),\n field: f.field,\n hashes,\n recordIdColumn,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n tokens: tokens.tokens,\n })\n this.logSearchDebug('search:cf-filter', {\n entity: String(entity),\n field: f.field,\n tokens: tokens.tokens,\n hashes,\n applied,\n tenantId: opts.tenantId ?? null,\n organizationScope: orgScope,\n })\n if (applied) continue\n } else {\n this.logSearchDebug('search:cf-skip-empty-hashes', {\n entity: String(entity),\n field: f.field,\n value: f.value,\n })\n }\n }\n switch (f.op) {\n case 'eq': q = q.where(expr, '=', f.value); break\n case 'ne': q = q.where(expr, '!=', f.value); break\n case 'gt': q = q.where(expr, '>', f.value); break\n case 'gte': q = q.where(expr, '>=', f.value); break\n case 'lt': q = q.where(expr, '<', f.value); break\n case 'lte': q = q.where(expr, '<=', f.value); break\n case 'in': q = q.whereIn(expr as any, f.value ?? []); break\n case 'nin': q = q.whereNotIn(expr as any, f.value ?? []); break\n case 'like': q = q.where(expr, 'like', f.value); break\n case 'ilike': q = q.where(expr, 'ilike', f.value); break\n case 'exists': f.value ? q = q.whereNotNull(expr) : q = q.whereNull(expr); break\n }\n }\n\n // Entity extensions joins (no selection yet; enables future filters/projections)\n if (opts.includeExtensions) {\n const { getModules } = await import('@open-mercato/shared/lib/i18n/server')\n const allMods = getModules() as any[]\n const allExts = allMods.flatMap((m) => (m as any).entityExtensions || [])\n const exts = allExts.filter((e: any) => e.base === entity)\n const chosen = Array.isArray(opts.includeExtensions)\n ? exts.filter((e: any) => (opts.includeExtensions as string[]).includes(e.extension))\n : exts\n for (const e of chosen) {\n const [, extName] = (e.extension as string).split(':')\n const extTable = extName.endsWith('s') ? extName : `${extName}s`\n const alias = `ext_${sanitize(extName)}`\n q = q.leftJoin({ [alias]: extTable }, function (this: any) {\n this.on(`${alias}.${e.join.extensionKey}`, '=', knex.raw('??', [`${table}.${e.join.baseKey}`]))\n })\n }\n }\n\n // Sorting: base fields and cf:* (use aggregated alias for cf)\n for (const s of opts.sort || []) {\n if (s.field.startsWith('cf:')) {\n const key = s.field.slice(3)\n const alias = sanitize(`cf:${key}`)\n // Ensure included in projection to sort by\n if (!cfSelectedAliases.includes(alias)) {\n const expr = cfValueExprByKey[key]\n if (expr) {\n q = q.select(knex.raw(`max(${expr.toString()}) as ??`, [alias]))\n cfSelectedAliases.push(alias)\n }\n }\n q = q.orderBy(alias, s.dir ?? 'asc')\n } else {\n const column = await this.resolveBaseColumn(table, s.field)\n if (!column) continue\n q = q.orderBy(qualify(column), s.dir ?? 'asc')\n }\n }\n\n // Pagination\n const page = opts.page?.page ?? 1\n const pageSize = opts.page?.pageSize ?? 20\n // Deduplicate if we joined CFs or extensions by grouping on base id\n if ((opts.includeExtensions && (Array.isArray(opts.includeExtensions) ? (opts.includeExtensions.length > 0) : true)) || Object.keys(cfValueExprByKey).length > 0) {\n q = q.groupBy(`${table}.id`)\n }\n const countClone: any = q.clone()\n if (typeof countClone.clearSelect === 'function') countClone.clearSelect()\n if (typeof countClone.clearOrder === 'function') countClone.clearOrder()\n if (typeof countClone.clearGroup === 'function') countClone.clearGroup()\n const countRow = await countClone\n .countDistinct(`${table}.id as count`)\n .first()\n const total = Number((countRow as any)?.count ?? 0)\n const items = await q.limit(pageSize).offset((page - 1) * pageSize)\n\n if (cfJsonAliases.size > 0) {\n for (const row of items as any[]) {\n for (const alias of cfJsonAliases) {\n const multiAlias = cfMultiAliasByAlias.get(alias)\n const isMulti = multiAlias ? Boolean(row[multiAlias]) : false\n let raw = row[alias]\n if (typeof raw === 'string') {\n try { raw = JSON.parse(raw) } catch { /* ignore malformed json */ }\n }\n if (isMulti) {\n if (raw == null) row[alias] = []\n else if (Array.isArray(raw)) row[alias] = raw\n else row[alias] = [raw]\n } else {\n if (Array.isArray(raw)) row[alias] = raw.length > 0 ? raw[0] : null\n else row[alias] = raw\n }\n if (multiAlias) delete row[multiAlias]\n }\n }\n }\n\n const svc = this.getEncryptionService()\n const decryptPayload =\n svc?.decryptEntityPayload?.bind(svc) as\n | ((\n entityId: EntityId,\n payload: Record<string, unknown>,\n tenantId: string | null,\n organizationId: string | null,\n ) => Promise<Record<string, unknown>>)\n | null\n let decryptedItems = items\n if (decryptPayload) {\n const fallbackOrgId =\n opts.organizationId\n ?? (Array.isArray(opts.organizationIds) && opts.organizationIds.length === 1 ? opts.organizationIds[0] : null)\n decryptedItems = await Promise.all(\n (items as any[]).map(async (item) => {\n try {\n const decrypted = await decryptPayload(\n entity,\n item,\n item?.tenant_id ?? item?.tenantId ?? opts.tenantId ?? null,\n item?.organization_id ?? item?.organizationId ?? fallbackOrgId ?? null,\n )\n return { ...item, ...decrypted }\n } catch (err) {\n console.error('QueryEngine: error decrypting entity payload', err);\n return item\n }\n })\n )\n }\n\n return { items: decryptedItems, page, pageSize, total }\n }\n\n private async resolveBaseColumn(table: string, field: string): Promise<string | null> {\n if (await this.columnExists(table, field)) return field\n if (field === 'organization_id' && await this.columnExists(table, 'id')) return 'id'\n return null\n }\n\n private async columnExists(table: string, column: string): Promise<boolean> {\n const key = `${table}.${column}`\n if (this.columnCache.has(key)) {\n const cached = this.columnCache.get(key)\n if (cached === true) return true\n this.columnCache.delete(key)\n }\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n const exists = await knex('information_schema.columns')\n .where({ table_name: table, column_name: column })\n .first()\n const present = !!exists\n if (present) this.columnCache.set(key, true)\n else this.columnCache.delete(key)\n return present\n }\n\n private async tableExists(table: string): Promise<boolean> {\n if (this.tableCache.has(table)) return this.tableCache.get(table) ?? false\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n const exists = await knex('information_schema.tables')\n .where({ table_name: table })\n .first()\n const present = !!exists\n this.tableCache.set(table, present)\n return present\n }\n\n private async hasSearchTokens(\n entity: string,\n tenantId: string | null,\n orgScope?: { ids: string[]; includeNull: boolean } | null\n ): Promise<boolean> {\n try {\n const knex = this.getKnexFn ? this.getKnexFn() : (this.em as any).getConnection().getKnex()\n const query = knex('search_tokens').select(1).where('entity_type', entity).limit(1)\n if (tenantId !== undefined) {\n query.andWhereRaw('tenant_id is not distinct from ?', [tenantId])\n }\n if (orgScope) {\n this.applyOrganizationScope(query as any, 'search_tokens.organization_id', orgScope)\n }\n const row = await query.first()\n return !!row\n } catch (err) {\n this.logSearchDebug('search:has-tokens-error', {\n entity,\n tenantId,\n organizationScope: orgScope,\n error: err instanceof Error ? err.message : String(err),\n })\n return false\n }\n }\n\n private applySearchTokens<TRecord extends ResultRow, TResult>(\n q: Knex.QueryBuilder<TRecord, TResult>,\n opts: {\n entity: string\n field: string\n hashes: string[]\n recordIdColumn: string\n tenantId?: string | null\n organizationScope?: { ids: string[]; includeNull: boolean } | null\n combineWith?: 'and' | 'or'\n tokens?: string[]\n }\n ): boolean {\n if (!opts.hashes.length) {\n this.logSearchDebug('search:skip-no-hashes', {\n entity: opts.entity,\n field: opts.field,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n })\n return false\n }\n const alias = `st_${this.searchAliasSeq++}`\n const combineWith = opts.combineWith === 'or' ? 'orWhereExists' : 'whereExists'\n const engine = this\n this.logSearchDebug('search:apply-search-tokens', {\n entity: opts.entity,\n field: opts.field,\n alias,\n tokenCount: opts.hashes.length,\n tokens: opts.tokens,\n tenantId: opts.tenantId ?? null,\n organizationScope: opts.organizationScope,\n combineWith: opts.combineWith ?? 'and',\n })\n ;(q as any)[combineWith](function (this: Knex.QueryBuilder) {\n this.select(1)\n .from({ [alias]: 'search_tokens' })\n .where(`${alias}.entity_type`, opts.entity)\n .andWhere(`${alias}.field`, opts.field)\n .andWhereRaw('?? = ??::text', [`${alias}.entity_id`, opts.recordIdColumn])\n .whereIn(`${alias}.token_hash`, opts.hashes)\n .groupBy(`${alias}.entity_id`, `${alias}.field`)\n .havingRaw(`count(distinct ${alias}.token_hash) >= ?`, [opts.hashes.length])\n if (opts.tenantId !== undefined) {\n this.andWhereRaw(`${alias}.tenant_id is not distinct from ?`, [opts.tenantId ?? null])\n }\n if (opts.organizationScope) {\n engine.applyOrganizationScope(this as any, `${alias}.organization_id`, opts.organizationScope)\n }\n })\n return true\n }\n\n private configureCustomFieldSources(\n q: any,\n baseTable: string,\n baseEntity: EntityId,\n knex: any,\n opts: QueryOptions,\n qualify: (column: string) => string\n ): ResolvedCustomFieldSource[] {\n const sources: ResolvedCustomFieldSource[] = [\n {\n entityId: baseEntity,\n alias: 'base',\n table: baseTable,\n recordIdExpr: knex.raw('??::text', [`${baseTable}.id`]),\n },\n ]\n const extras: QueryCustomFieldSource[] = opts.customFieldSources ?? []\n extras.forEach((srcOpt, index) => {\n const joinTable = srcOpt.table ?? resolveEntityTableName(this.em, srcOpt.entityId)\n const alias = srcOpt.alias ?? `cfs_${index}`\n const join = srcOpt.join\n if (!join) {\n throw new Error(`QueryEngine: customFieldSources entry for ${String(srcOpt.entityId)} requires a join configuration`)\n }\n const joinArgs = { [alias]: joinTable }\n const joinCallback = function (this: any) {\n this.on(`${alias}.${join.toField}`, '=', qualify(join.fromField))\n }\n const joinType = join.type ?? 'left'\n if (joinType === 'inner') q.join(joinArgs, joinCallback)\n else q.leftJoin(joinArgs, joinCallback)\n const recordColumn = srcOpt.recordIdColumn ?? 'id'\n sources.push({\n entityId: srcOpt.entityId,\n alias,\n table: joinTable,\n recordIdExpr: knex.raw('??::text', [`${alias}.${recordColumn}`]),\n })\n })\n return sources\n }\n\n private logSearchDebug(event: string, payload: Record<string, unknown>) {\n try {\n console.info('[query:search]', event, JSON.stringify(payload))\n } catch {\n console.info('[query:search]', event, payload)\n }\n }\n\n private resolveOrganizationScope(opts: QueryOptions): { ids: string[]; includeNull: boolean } | null {\n if (opts.organizationIds !== undefined) {\n const raw = (opts.organizationIds ?? []).map((id) => (typeof id === 'string' ? id.trim() : id))\n const includeNull = raw.some((id) => id == null || id === '')\n const ids = raw.filter((id): id is string => typeof id === 'string' && id.length > 0)\n return { ids: Array.from(new Set(ids)), includeNull }\n }\n if (typeof opts.organizationId === 'string' && opts.organizationId.trim().length > 0) {\n return { ids: [opts.organizationId], includeNull: false }\n }\n return null\n }\n\n private applyOrganizationScope(q: any, column: string, scope: { ids: string[]; includeNull: boolean }): any {\n if (!scope) return q\n if (scope.ids.length === 0 && !scope.includeNull) {\n return q.whereRaw('1 = 0')\n }\n return q.where((builder: any) => {\n let applied = false\n if (scope.ids.length > 0) {\n builder.whereIn(column as any, scope.ids)\n applied = true\n }\n if (scope.includeNull) {\n if (applied) builder.orWhereNull(column)\n else builder.whereNull(column)\n applied = true\n }\n if (!applied) builder.whereRaw('1 = 0')\n })\n }\n\n}\n const computeScore = (cfg: Record<string, unknown>, kind: string, entityIndex: number) => {\n const listVisibleScore = cfg.listVisible === false ? 0 : 1\n const formEditableScore = cfg.formEditable === false ? 0 : 1\n const filterableScore = cfg.filterable ? 1 : 0\n const kindScore = (() => {\n switch (kind) {\n case 'dictionary':\n return 8\n case 'relation':\n return 6\n case 'select':\n return 4\n case 'multiline':\n return 3\n case 'boolean':\n case 'integer':\n case 'float':\n return 2\n default:\n return 1\n }\n })()\n const optionsBonus = Array.isArray(cfg.options) && cfg.options.length ? 2 : 0\n const dictionaryBonus = typeof cfg.dictionaryId === 'string' && cfg.dictionaryId.trim().length ? 5 : 0\n const base = (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus\n const penalty = typeof cfg.priority === 'number' ? cfg.priority : 0\n return { base, penalty, entityIndex }\n }\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,195 @@
1
+ function normalizeFilters(filters) {
2
+ if (!filters) return [];
3
+ const normalizeField = (key) => key.startsWith("cf_") ? `cf:${key.slice(3)}` : key;
4
+ if (Array.isArray(filters)) {
5
+ return filters.map((f) => ({
6
+ ...f,
7
+ field: normalizeField(String(f.field))
8
+ }));
9
+ }
10
+ const out = [];
11
+ const obj = filters;
12
+ const push = (field, op, value) => {
13
+ out.push({ field, op, value });
14
+ };
15
+ for (const [rawKey, rawVal] of Object.entries(obj)) {
16
+ const field = normalizeField(rawKey);
17
+ if (rawVal !== null && typeof rawVal === "object" && !Array.isArray(rawVal)) {
18
+ for (const [opKey, opVal] of Object.entries(rawVal)) {
19
+ switch (opKey) {
20
+ case "$eq":
21
+ push(field, "eq", opVal);
22
+ break;
23
+ case "$ne":
24
+ push(field, "ne", opVal);
25
+ break;
26
+ case "$gt":
27
+ push(field, "gt", opVal);
28
+ break;
29
+ case "$gte":
30
+ push(field, "gte", opVal);
31
+ break;
32
+ case "$lt":
33
+ push(field, "lt", opVal);
34
+ break;
35
+ case "$lte":
36
+ push(field, "lte", opVal);
37
+ break;
38
+ case "$in":
39
+ push(field, "in", opVal);
40
+ break;
41
+ case "$nin":
42
+ push(field, "nin", opVal);
43
+ break;
44
+ case "$like":
45
+ push(field, "like", opVal);
46
+ break;
47
+ case "$ilike":
48
+ push(field, "ilike", opVal);
49
+ break;
50
+ case "$exists":
51
+ push(field, "exists", opVal);
52
+ break;
53
+ }
54
+ }
55
+ } else {
56
+ push(field, "eq", rawVal);
57
+ }
58
+ }
59
+ return out;
60
+ }
61
+ function resolveJoins(baseTable, joins, resolveTable) {
62
+ if (!joins || joins.length === 0) return [];
63
+ const resolved = [];
64
+ const seen = /* @__PURE__ */ new Set();
65
+ for (const entry of joins) {
66
+ if (!entry || typeof entry !== "object") continue;
67
+ const alias = typeof entry.alias === "string" ? entry.alias.trim() : "";
68
+ if (!alias) continue;
69
+ if (seen.has(alias)) continue;
70
+ const table = entry.table ?? (entry.entityId ? resolveTable(String(entry.entityId)) : null);
71
+ if (!table) continue;
72
+ const fromField = entry.from?.field?.trim();
73
+ const toField = entry.to?.field?.trim();
74
+ if (!fromField || !toField) continue;
75
+ const fromAliasRaw = entry.from?.alias?.trim();
76
+ const fromAlias = fromAliasRaw && fromAliasRaw.length > 0 ? fromAliasRaw : "base";
77
+ const type = entry.type === "inner" ? "inner" : "left";
78
+ resolved.push({ alias, table, fromAlias, fromField, toField, type });
79
+ seen.add(alias);
80
+ }
81
+ return resolved;
82
+ }
83
+ function buildJoinChain(alias, joinMap, baseTable, visited = /* @__PURE__ */ new Set()) {
84
+ if (visited.has(alias)) {
85
+ throw new Error(`QueryEngine: circular join reference detected for alias ${alias}`);
86
+ }
87
+ const cfg = joinMap.get(alias);
88
+ if (!cfg) return [];
89
+ visited.add(alias);
90
+ if (!cfg.fromAlias || cfg.fromAlias === "base" || cfg.fromAlias === baseTable) {
91
+ return [cfg];
92
+ }
93
+ const parentChain = buildJoinChain(cfg.fromAlias, joinMap, baseTable, visited);
94
+ if (parentChain.length === 0) return [];
95
+ return [...parentChain, cfg];
96
+ }
97
+ function partitionFilters(baseTable, filters, joinMap) {
98
+ const baseFilters = [];
99
+ const joinFilters = /* @__PURE__ */ new Map();
100
+ for (const filter of filters) {
101
+ const field = String(filter.field);
102
+ if (field.startsWith("cf:")) continue;
103
+ const parts = field.split(".");
104
+ if (parts.length === 2) {
105
+ const [aliasNameRaw, column] = parts;
106
+ const aliasName = aliasNameRaw || "";
107
+ if (joinMap.has(aliasName)) {
108
+ const list = joinFilters.get(aliasName) ?? [];
109
+ list.push({ alias: aliasName, column, op: filter.op, value: filter.value });
110
+ joinFilters.set(aliasName, list);
111
+ continue;
112
+ }
113
+ if (aliasName === baseTable || aliasName === "base") {
114
+ baseFilters.push({
115
+ field: column,
116
+ op: filter.op,
117
+ value: filter.value,
118
+ qualified: `${baseTable}.${column}`
119
+ });
120
+ continue;
121
+ }
122
+ }
123
+ baseFilters.push({ ...filter });
124
+ }
125
+ return { baseFilters, joinFilters };
126
+ }
127
+ async function applyJoinFilters({
128
+ knex,
129
+ baseTable,
130
+ builder,
131
+ joinMap,
132
+ joinFilters,
133
+ aliasTables,
134
+ qualifyBase,
135
+ applyAliasScope,
136
+ applyFilterOp,
137
+ columnExists
138
+ }) {
139
+ const resolveAliasName = (aliasName) => {
140
+ if (!aliasName || aliasName === "base") return baseTable;
141
+ return aliasName;
142
+ };
143
+ for (const [alias, filtersForAlias] of joinFilters.entries()) {
144
+ const chain = buildJoinChain(alias, joinMap, baseTable);
145
+ if (!chain.length) continue;
146
+ const first = chain[0];
147
+ const sub = knex({ [first.alias]: first.table }).select(1);
148
+ await applyAliasScope(sub, first.alias, first.table);
149
+ const parentAlias = resolveAliasName(first.fromAlias);
150
+ if (parentAlias === baseTable) {
151
+ sub.whereRaw("?? = ??", [`${first.alias}.${first.toField}`, qualifyBase(first.fromField)]);
152
+ } else {
153
+ sub.whereRaw("?? = ??", [`${first.alias}.${first.toField}`, `${parentAlias}.${first.fromField}`]);
154
+ }
155
+ for (const cfg of chain.slice(1)) {
156
+ const joinArgs = { [cfg.alias]: cfg.table };
157
+ const parent = resolveAliasName(cfg.fromAlias);
158
+ const joinFn = function() {
159
+ const left = `${cfg.alias}.${cfg.toField}`;
160
+ const right = parent === baseTable ? qualifyBase(cfg.fromField) : `${parent}.${cfg.fromField}`;
161
+ this.on(knex.raw("?? = ??", [left, right]));
162
+ };
163
+ if (cfg.type === "inner") sub.join(joinArgs, joinFn);
164
+ else sub.leftJoin(joinArgs, joinFn);
165
+ await applyAliasScope(sub, cfg.alias, cfg.table);
166
+ }
167
+ let existsDirective = null;
168
+ for (const filter of filtersForAlias) {
169
+ if (filter.op === "exists") {
170
+ if (filter.value === false) existsDirective = false;
171
+ else if (existsDirective === null) existsDirective = true;
172
+ continue;
173
+ }
174
+ const targetTable = aliasTables.get(filter.alias);
175
+ if (!targetTable) continue;
176
+ if (columnExists) {
177
+ const exists = await columnExists(targetTable, filter.column);
178
+ if (!exists) continue;
179
+ }
180
+ const qualified = `${filter.alias}.${filter.column}`;
181
+ applyFilterOp(sub, qualified, filter.op, filter.value);
182
+ }
183
+ if (existsDirective === false) builder = builder.whereNotExists(sub);
184
+ else builder = builder.whereExists(sub);
185
+ }
186
+ return builder;
187
+ }
188
+ export {
189
+ applyJoinFilters,
190
+ buildJoinChain,
191
+ normalizeFilters,
192
+ partitionFilters,
193
+ resolveJoins
194
+ };
195
+ //# sourceMappingURL=join-utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/query/join-utils.ts"],
4
+ "sourcesContent": ["import type { Knex } from 'knex'\nimport type { QueryOptions, QueryJoinEdge } from './types'\nimport type { FilterOp } from './types'\n\nexport type NormalizedFilter = { field: string; op: FilterOp; value?: unknown }\n\nexport function normalizeFilters(filters?: QueryOptions['filters']): NormalizedFilter[] {\n if (!filters) return []\n const normalizeField = (key: string) => (key.startsWith('cf_') ? `cf:${key.slice(3)}` : key)\n if (Array.isArray(filters)) {\n return (filters as any[]).map((f) => ({\n ...f,\n field: normalizeField(String((f as any).field)),\n }))\n }\n const out: NormalizedFilter[] = []\n const obj = filters as Record<string, unknown>\n const push = (field: string, op: FilterOp, value?: unknown) => {\n out.push({ field, op, value })\n }\n for (const [rawKey, rawVal] of Object.entries(obj)) {\n const field = normalizeField(rawKey)\n if (rawVal !== null && typeof rawVal === 'object' && !Array.isArray(rawVal)) {\n for (const [opKey, opVal] of Object.entries(rawVal as Record<string, unknown>)) {\n switch (opKey) {\n case '$eq':\n push(field, 'eq', opVal)\n break\n case '$ne':\n push(field, 'ne', opVal)\n break\n case '$gt':\n push(field, 'gt', opVal)\n break\n case '$gte':\n push(field, 'gte', opVal)\n break\n case '$lt':\n push(field, 'lt', opVal)\n break\n case '$lte':\n push(field, 'lte', opVal)\n break\n case '$in':\n push(field, 'in', opVal)\n break\n case '$nin':\n push(field, 'nin', opVal)\n break\n case '$like':\n push(field, 'like', opVal)\n break\n case '$ilike':\n push(field, 'ilike', opVal)\n break\n case '$exists':\n push(field, 'exists', opVal)\n break\n }\n }\n } else {\n push(field, 'eq', rawVal)\n }\n }\n return out\n}\n\nexport type ResolvedJoin = {\n alias: string\n table: string\n fromAlias: string\n fromField: string\n toField: string\n type: 'left' | 'inner'\n}\n\nexport type BaseFilter = NormalizedFilter & { qualified?: string }\nexport type JoinFilter = { alias: string; column: string; op: FilterOp; value?: unknown }\n\nexport function resolveJoins(\n baseTable: string,\n joins: QueryJoinEdge[] | null | undefined,\n resolveTable: (entityId: string) => string | null,\n): ResolvedJoin[] {\n if (!joins || joins.length === 0) return []\n const resolved: ResolvedJoin[] = []\n const seen = new Set<string>()\n for (const entry of joins) {\n if (!entry || typeof entry !== 'object') continue\n const alias = typeof entry.alias === 'string' ? entry.alias.trim() : ''\n if (!alias) continue\n if (seen.has(alias)) continue\n const table =\n entry.table ??\n (entry.entityId ? resolveTable(String(entry.entityId)) : null)\n if (!table) continue\n const fromField = entry.from?.field?.trim()\n const toField = entry.to?.field?.trim()\n if (!fromField || !toField) continue\n const fromAliasRaw = entry.from?.alias?.trim()\n const fromAlias = fromAliasRaw && fromAliasRaw.length > 0 ? fromAliasRaw : 'base'\n const type: 'left' | 'inner' = entry.type === 'inner' ? 'inner' : 'left'\n resolved.push({ alias, table, fromAlias, fromField, toField, type })\n seen.add(alias)\n }\n return resolved\n}\n\nexport function buildJoinChain(\n alias: string,\n joinMap: Map<string, ResolvedJoin>,\n baseTable: string,\n visited: Set<string> = new Set(),\n): ResolvedJoin[] {\n if (visited.has(alias)) {\n throw new Error(`QueryEngine: circular join reference detected for alias ${alias}`)\n }\n const cfg = joinMap.get(alias)\n if (!cfg) return []\n visited.add(alias)\n if (!cfg.fromAlias || cfg.fromAlias === 'base' || cfg.fromAlias === baseTable) {\n return [cfg]\n }\n const parentChain = buildJoinChain(cfg.fromAlias, joinMap, baseTable, visited)\n if (parentChain.length === 0) return []\n return [...parentChain, cfg]\n}\n\nexport function partitionFilters(\n baseTable: string,\n filters: NormalizedFilter[],\n joinMap: Map<string, ResolvedJoin>,\n): { baseFilters: BaseFilter[]; joinFilters: Map<string, JoinFilter[]> } {\n const baseFilters: BaseFilter[] = []\n const joinFilters = new Map<string, JoinFilter[]>()\n for (const filter of filters) {\n const field = String(filter.field)\n if (field.startsWith('cf:')) continue\n const parts = field.split('.')\n if (parts.length === 2) {\n const [aliasNameRaw, column] = parts\n const aliasName = aliasNameRaw || ''\n if (joinMap.has(aliasName)) {\n const list = joinFilters.get(aliasName) ?? []\n list.push({ alias: aliasName, column, op: filter.op, value: filter.value })\n joinFilters.set(aliasName, list)\n continue\n }\n if (aliasName === baseTable || aliasName === 'base') {\n baseFilters.push({\n field: column,\n op: filter.op,\n value: filter.value,\n qualified: `${baseTable}.${column}`,\n })\n continue\n }\n }\n baseFilters.push({ ...filter })\n }\n return { baseFilters, joinFilters }\n}\n\ntype ApplyJoinFiltersOptions = {\n knex: Knex\n baseTable: string\n builder: Knex.QueryBuilder\n joinMap: Map<string, ResolvedJoin>\n joinFilters: Map<string, JoinFilter[]>\n aliasTables: Map<string, string>\n qualifyBase: (column: string) => string\n applyAliasScope: (builder: Knex.QueryBuilder, alias: string, table: string) => Promise<void> | void\n applyFilterOp: (builder: Knex.QueryBuilder, column: string, op: FilterOp, value?: unknown) => void\n columnExists?: (table: string, column: string) => Promise<boolean> | boolean\n}\n\nexport async function applyJoinFilters({\n knex,\n baseTable,\n builder,\n joinMap,\n joinFilters,\n aliasTables,\n qualifyBase,\n applyAliasScope,\n applyFilterOp,\n columnExists,\n}: ApplyJoinFiltersOptions): Promise<Knex.QueryBuilder> {\n const resolveAliasName = (aliasName?: string | null) => {\n if (!aliasName || aliasName === 'base') return baseTable\n return aliasName\n }\n\n for (const [alias, filtersForAlias] of joinFilters.entries()) {\n const chain = buildJoinChain(alias, joinMap, baseTable)\n if (!chain.length) continue\n const first = chain[0]\n const sub = knex({ [first.alias]: first.table }).select(1)\n await applyAliasScope(sub, first.alias, first.table)\n const parentAlias = resolveAliasName(first.fromAlias)\n if (parentAlias === baseTable) {\n sub.whereRaw('?? = ??', [`${first.alias}.${first.toField}`, qualifyBase(first.fromField)])\n } else {\n sub.whereRaw('?? = ??', [`${first.alias}.${first.toField}`, `${parentAlias}.${first.fromField}`])\n }\n for (const cfg of chain.slice(1)) {\n const joinArgs = { [cfg.alias]: cfg.table }\n const parent = resolveAliasName(cfg.fromAlias)\n const joinFn = function (this: Knex.JoinClause) {\n const left = `${cfg.alias}.${cfg.toField}`\n const right = parent === baseTable ? qualifyBase(cfg.fromField) : `${parent}.${cfg.fromField}`\n this.on(knex.raw('?? = ??', [left, right]))\n }\n if (cfg.type === 'inner') sub.join(joinArgs, joinFn)\n else sub.leftJoin(joinArgs, joinFn)\n await applyAliasScope(sub, cfg.alias, cfg.table)\n }\n let existsDirective: boolean | null = null\n for (const filter of filtersForAlias) {\n if (filter.op === 'exists') {\n if (filter.value === false) existsDirective = false\n else if (existsDirective === null) existsDirective = true\n continue\n }\n const targetTable = aliasTables.get(filter.alias)\n if (!targetTable) continue\n if (columnExists) {\n const exists = await columnExists(targetTable, filter.column)\n if (!exists) continue\n }\n const qualified = `${filter.alias}.${filter.column}`\n applyFilterOp(sub, qualified, filter.op, filter.value)\n }\n if (existsDirective === false) builder = builder.whereNotExists(sub)\n else builder = builder.whereExists(sub)\n }\n return builder\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,9 @@
1
+ var SortDir = /* @__PURE__ */ ((SortDir2) => {
2
+ SortDir2["Asc"] = "asc";
3
+ SortDir2["Desc"] = "desc";
4
+ return SortDir2;
5
+ })(SortDir || {});
6
+ export {
7
+ SortDir
8
+ };
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/query/types.ts"],
4
+ "sourcesContent": ["import type { EntityId } from '@open-mercato/shared/modules/entities'\nimport type { Profiler } from '../profiler'\n\nexport type FilterOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'like' | 'ilike' | 'exists'\n\nexport enum SortDir {\n Asc = 'asc',\n Desc = 'desc',\n}\n\nexport type FieldSelector = string // base field or custom field key (prefixed with 'cf:')\n\nexport type Filter = {\n field: FieldSelector\n op: FilterOp\n value?: any\n}\n\nexport type Sort = { field: FieldSelector; dir?: SortDir }\n\nexport type Page = { page?: number; pageSize?: number }\n\n// Mongo/Medusa-style filter operators (typed)\nexport type WhereOps<T> = {\n $eq?: T\n $ne?: T | null\n $gt?: T extends number | Date ? T : never\n $gte?: T extends number | Date ? T : never\n $lt?: T extends number | Date ? T : never\n $lte?: T extends number | Date ? T : never\n $in?: T[]\n $nin?: T[]\n $like?: T extends string ? string : never\n $ilike?: T extends string ? string : never\n $exists?: boolean\n}\n\n// A field filter can be a direct value (equals) or ops object\nexport type WhereValue<T = any> = T | WhereOps<T>\n\n// Generic shape for object filters. If you have a typed map of field\u2192type,\n// pass it as the generic to get end-to-end typing.\n// Example: Where<{\n// id: string; title: string; created_at: Date; 'cf:severity': number\n// }>\nexport type Where<Fields extends Record<string, any> = Record<string, any>> =\n Partial<{ [K in keyof Fields]: WhereValue<Fields[K]> }> & Record<string, WhereValue>\n\nexport type QueryCustomFieldJoin = {\n fromField: string\n toField: string\n type?: 'left' | 'inner'\n}\n\nexport type QueryCustomFieldSource = {\n entityId: EntityId\n table?: string\n alias?: string\n recordIdColumn?: string\n join?: QueryCustomFieldJoin\n tenantField?: string\n organizationField?: string\n}\n\nexport type QueryJoinEdge = {\n alias: string\n table?: string\n entityId?: EntityId\n from: {\n alias?: string\n field: string\n }\n to: {\n field: string\n }\n type?: 'left' | 'inner'\n}\n\nexport type QueryOptions = {\n fields?: FieldSelector[] // base fields and/or 'cf:<key>' for custom fields\n includeExtensions?: boolean | string[] // include all registered extensions or only specific ones by entity id\n includeCustomFields?: boolean | string[] // include all CFs or specific keys\n // Accept classic array syntax or Mongo-style object syntax\n filters?: Filter[] | Where\n sort?: Sort[]\n page?: Page\n organizationId?: string // enforce multi-tenant scope\n tenantId?: string // enforce tenant scope\n // Optional list of organization ids to scope results. Takes precedence over organizationId.\n organizationIds?: string[]\n // Soft-delete behavior: when false (default), rows with non-null deleted_at\n // are excluded if the base table has that column. Set true to include them.\n withDeleted?: boolean\n customFieldSources?: QueryCustomFieldSource[]\n joins?: QueryJoinEdge[]\n profiler?: Profiler\n}\n\nexport type PartialIndexWarning = {\n entity: EntityId\n entityLabel?: string | null\n baseCount?: number | null\n indexedCount?: number | null\n scope?: 'scoped' | 'global'\n}\n\nexport type QueryResultMeta = {\n partialIndexWarning?: PartialIndexWarning\n}\n\nexport type QueryResult<T = any> = {\n items: T[]\n page: number\n pageSize: number\n total: number\n meta?: QueryResultMeta\n}\n\nexport interface QueryEngine {\n query<T = any>(entity: EntityId, opts?: QueryOptions): Promise<QueryResult<T>>\n}\n"],
5
+ "mappings": "AAKO,IAAK,UAAL,kBAAKA,aAAL;AACL,EAAAA,SAAA,SAAM;AACN,EAAAA,SAAA,UAAO;AAFG,SAAAA;AAAA,GAAA;",
6
+ "names": ["SortDir"]
7
+ }
@@ -0,0 +1,32 @@
1
+ import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
2
+ const DEFAULT_BLOCKLIST = ["password", "token", "secret", "hash"];
3
+ function parseBoolean(raw, fallback) {
4
+ return parseBooleanWithDefault(raw, fallback);
5
+ }
6
+ function parseNumber(raw, fallback, min = 1) {
7
+ if (raw == null) return fallback;
8
+ const value = Number.parseInt(raw, 10);
9
+ if (!Number.isFinite(value)) return fallback;
10
+ if (value < min) return fallback;
11
+ return value;
12
+ }
13
+ function parseHashAlgorithm(raw) {
14
+ const value = (raw ?? "").trim().toLowerCase();
15
+ if (value === "sha1") return "sha1";
16
+ if (value === "md5") return "md5";
17
+ return "sha256";
18
+ }
19
+ function resolveSearchConfig() {
20
+ return {
21
+ enabled: parseBoolean(process.env.OM_SEARCH_ENABLED, true),
22
+ minTokenLength: parseNumber(process.env.OM_SEARCH_MIN_LEN, 3, 1),
23
+ enablePartials: parseBoolean(process.env.OM_SEARCH_ENABLE_PARTIAL, true),
24
+ hashAlgorithm: parseHashAlgorithm(process.env.OM_SEARCH_HASH_ALGO),
25
+ storeRawTokens: parseBoolean(process.env.OM_SEARCH_STORE_RAW_TOKENS, false),
26
+ blocklistedFields: (process.env.OM_SEARCH_FIELD_BLOCKLIST ?? "").split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).filter((value, index, arr) => arr.indexOf(value) === index).map((entry) => entry.toLowerCase()).concat(DEFAULT_BLOCKLIST).filter((value, index, arr) => arr.indexOf(value) === index)
27
+ };
28
+ }
29
+ export {
30
+ resolveSearchConfig
31
+ };
32
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/search/config.ts"],
4
+ "sourcesContent": ["import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\n\nexport type SearchConfig = {\n enabled: boolean\n minTokenLength: number\n enablePartials: boolean\n hashAlgorithm: 'sha256' | 'sha1' | 'md5'\n storeRawTokens: boolean\n blocklistedFields: string[]\n}\n\nconst DEFAULT_BLOCKLIST = ['password', 'token', 'secret', 'hash']\n\nfunction parseBoolean(raw: string | undefined, fallback: boolean): boolean {\n return parseBooleanWithDefault(raw, fallback)\n}\n\nfunction parseNumber(raw: string | undefined, fallback: number, min = 1): number {\n if (raw == null) return fallback\n const value = Number.parseInt(raw, 10)\n if (!Number.isFinite(value)) return fallback\n if (value < min) return fallback\n return value\n}\n\nfunction parseHashAlgorithm(raw: string | undefined): 'sha256' | 'sha1' | 'md5' {\n const value = (raw ?? '').trim().toLowerCase()\n if (value === 'sha1') return 'sha1'\n if (value === 'md5') return 'md5'\n return 'sha256'\n}\n\nexport function resolveSearchConfig(): SearchConfig {\n return {\n enabled: parseBoolean(process.env.OM_SEARCH_ENABLED, true),\n minTokenLength: parseNumber(process.env.OM_SEARCH_MIN_LEN, 3, 1),\n enablePartials: parseBoolean(process.env.OM_SEARCH_ENABLE_PARTIAL, true),\n hashAlgorithm: parseHashAlgorithm(process.env.OM_SEARCH_HASH_ALGO),\n storeRawTokens: parseBoolean(process.env.OM_SEARCH_STORE_RAW_TOKENS, false),\n blocklistedFields: (process.env.OM_SEARCH_FIELD_BLOCKLIST ?? '')\n .split(',')\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0)\n .filter((value, index, arr) => arr.indexOf(value) === index)\n .map((entry) => entry.toLowerCase())\n .concat(DEFAULT_BLOCKLIST)\n .filter((value, index, arr) => arr.indexOf(value) === index),\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,+BAA+B;AAWxC,MAAM,oBAAoB,CAAC,YAAY,SAAS,UAAU,MAAM;AAEhE,SAAS,aAAa,KAAyB,UAA4B;AACzE,SAAO,wBAAwB,KAAK,QAAQ;AAC9C;AAEA,SAAS,YAAY,KAAyB,UAAkB,MAAM,GAAW;AAC/E,MAAI,OAAO,KAAM,QAAO;AACxB,QAAM,QAAQ,OAAO,SAAS,KAAK,EAAE;AACrC,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,QAAQ,IAAK,QAAO;AACxB,SAAO;AACT;AAEA,SAAS,mBAAmB,KAAoD;AAC9E,QAAM,SAAS,OAAO,IAAI,KAAK,EAAE,YAAY;AAC7C,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,UAAU,MAAO,QAAO;AAC5B,SAAO;AACT;AAEO,SAAS,sBAAoC;AAClD,SAAO;AAAA,IACL,SAAS,aAAa,QAAQ,IAAI,mBAAmB,IAAI;AAAA,IACzD,gBAAgB,YAAY,QAAQ,IAAI,mBAAmB,GAAG,CAAC;AAAA,IAC/D,gBAAgB,aAAa,QAAQ,IAAI,0BAA0B,IAAI;AAAA,IACvE,eAAe,mBAAmB,QAAQ,IAAI,mBAAmB;AAAA,IACjE,gBAAgB,aAAa,QAAQ,IAAI,4BAA4B,KAAK;AAAA,IAC1E,oBAAoB,QAAQ,IAAI,6BAA6B,IAC1D,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC,EAClC,OAAO,CAAC,OAAO,OAAO,QAAQ,IAAI,QAAQ,KAAK,MAAM,KAAK,EAC1D,IAAI,CAAC,UAAU,MAAM,YAAY,CAAC,EAClC,OAAO,iBAAiB,EACxB,OAAO,CAAC,OAAO,OAAO,QAAQ,IAAI,QAAQ,KAAK,MAAM,KAAK;AAAA,EAC/D;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,34 @@
1
+ import crypto from "crypto";
2
+ import { resolveSearchConfig } from "./config.js";
3
+ function normalizeText(text) {
4
+ return text.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[%_]/g, " ").toLowerCase();
5
+ }
6
+ function splitTokens(text, minLength) {
7
+ return normalizeText(text).split(/[^a-z0-9]+/i).filter((token) => token.length >= minLength);
8
+ }
9
+ function expandToken(token, config) {
10
+ if (!config.enablePartials) return [token];
11
+ const results = [];
12
+ for (let i = config.minTokenLength; i <= token.length; i += 1) {
13
+ results.push(token.slice(0, i));
14
+ }
15
+ return results;
16
+ }
17
+ function hashToken(token, config) {
18
+ const cfg = config ?? resolveSearchConfig();
19
+ return crypto.createHash(cfg.hashAlgorithm).update(token).digest("hex");
20
+ }
21
+ function tokenizeText(text, config) {
22
+ const cfg = config ?? resolveSearchConfig();
23
+ const baseTokens = splitTokens(text, cfg.minTokenLength);
24
+ const expanded = baseTokens.flatMap((token) => expandToken(token, cfg));
25
+ const unique = Array.from(new Set(expanded));
26
+ const tokens = unique.filter((token) => token.length >= cfg.minTokenLength);
27
+ const hashes = tokens.map((token) => hashToken(token, cfg));
28
+ return { tokens, hashes };
29
+ }
30
+ export {
31
+ hashToken,
32
+ tokenizeText
33
+ };
34
+ //# sourceMappingURL=tokenize.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/search/tokenize.ts"],
4
+ "sourcesContent": ["import crypto from 'crypto'\nimport { resolveSearchConfig, type SearchConfig } from './config'\n\nexport type TokenizationResult = {\n tokens: string[]\n hashes: string[]\n}\n\nfunction normalizeText(text: string): string {\n return text\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[%_]/g, ' ')\n .toLowerCase()\n}\n\nfunction splitTokens(text: string, minLength: number): string[] {\n return normalizeText(text)\n .split(/[^a-z0-9]+/i)\n .filter((token) => token.length >= minLength)\n}\n\nfunction expandToken(token: string, config: SearchConfig): string[] {\n if (!config.enablePartials) return [token]\n const results: string[] = []\n for (let i = config.minTokenLength; i <= token.length; i += 1) {\n results.push(token.slice(0, i))\n }\n return results\n}\n\nexport function hashToken(token: string, config?: SearchConfig): string {\n const cfg = config ?? resolveSearchConfig()\n return crypto.createHash(cfg.hashAlgorithm).update(token).digest('hex')\n}\n\nexport function tokenizeText(text: string, config?: SearchConfig): TokenizationResult {\n const cfg = config ?? resolveSearchConfig()\n const baseTokens = splitTokens(text, cfg.minTokenLength)\n const expanded = baseTokens.flatMap((token) => expandToken(token, cfg))\n const unique = Array.from(new Set(expanded))\n const tokens = unique.filter((token) => token.length >= cfg.minTokenLength)\n const hashes = tokens.map((token) => hashToken(token, cfg))\n return { tokens, hashes }\n}\n"],
5
+ "mappings": "AAAA,OAAO,YAAY;AACnB,SAAS,2BAA8C;AAOvD,SAAS,cAAc,MAAsB;AAC3C,SAAO,KACJ,UAAU,MAAM,EAChB,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,SAAS,GAAG,EACpB,YAAY;AACjB;AAEA,SAAS,YAAY,MAAc,WAA6B;AAC9D,SAAO,cAAc,IAAI,EACtB,MAAM,aAAa,EACnB,OAAO,CAAC,UAAU,MAAM,UAAU,SAAS;AAChD;AAEA,SAAS,YAAY,OAAe,QAAgC;AAClE,MAAI,CAAC,OAAO,eAAgB,QAAO,CAAC,KAAK;AACzC,QAAM,UAAoB,CAAC;AAC3B,WAAS,IAAI,OAAO,gBAAgB,KAAK,MAAM,QAAQ,KAAK,GAAG;AAC7D,YAAQ,KAAK,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,EAChC;AACA,SAAO;AACT;AAEO,SAAS,UAAU,OAAe,QAA+B;AACtE,QAAM,MAAM,UAAU,oBAAoB;AAC1C,SAAO,OAAO,WAAW,IAAI,aAAa,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxE;AAEO,SAAS,aAAa,MAAc,QAA2C;AACpF,QAAM,MAAM,UAAU,oBAAoB;AAC1C,QAAM,aAAa,YAAY,MAAM,IAAI,cAAc;AACvD,QAAM,WAAW,WAAW,QAAQ,CAAC,UAAU,YAAY,OAAO,GAAG,CAAC;AACtE,QAAM,SAAS,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC;AAC3C,QAAM,SAAS,OAAO,OAAO,CAAC,UAAU,MAAM,UAAU,IAAI,cAAc;AAC1E,QAAM,SAAS,OAAO,IAAI,CAAC,UAAU,UAAU,OAAO,GAAG,CAAC;AAC1D,SAAO,EAAE,QAAQ,OAAO;AAC1B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,24 @@
1
+ const DEFAULT_REPLACEMENT = "-";
2
+ const DEFAULT_ALLOWED_CHARS = "-";
3
+ const escapeRegex = (value) => value.replace(/[\\^$.*+?()[\]{}|\-]/g, "\\$&");
4
+ function slugify(value, options = {}) {
5
+ const replacement = options.replacement ?? DEFAULT_REPLACEMENT;
6
+ const allowedChars = options.allowedChars ?? DEFAULT_ALLOWED_CHARS;
7
+ const trimReplacement = options.trimReplacement ?? true;
8
+ const normalized = value.toLowerCase().trim();
9
+ if (!normalized) return "";
10
+ const escapedAllowed = escapeRegex(allowedChars);
11
+ const invalidPattern = new RegExp(`[^a-z0-9${escapedAllowed}]+`, "g");
12
+ const replaced = normalized.replace(invalidPattern, replacement);
13
+ if (!trimReplacement || replacement.length !== 1 || !replaced) return replaced;
14
+ const char = replacement;
15
+ let start = 0;
16
+ let end = replaced.length;
17
+ while (start < end && replaced[start] === char) start += 1;
18
+ while (end > start && replaced[end - 1] === char) end -= 1;
19
+ return replaced.slice(start, end);
20
+ }
21
+ export {
22
+ slugify
23
+ };
24
+ //# sourceMappingURL=slugify.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/slugify.ts"],
4
+ "sourcesContent": ["export type SlugifyOptions = {\n replacement?: string\n allowedChars?: string\n trimReplacement?: boolean\n}\n\nconst DEFAULT_REPLACEMENT = '-'\nconst DEFAULT_ALLOWED_CHARS = '-'\n\nconst escapeRegex = (value: string): string => value.replace(/[\\\\^$.*+?()[\\]{}|\\-]/g, '\\\\$&')\n\nexport function slugify(value: string, options: SlugifyOptions = {}): string {\n const replacement = options.replacement ?? DEFAULT_REPLACEMENT\n const allowedChars = options.allowedChars ?? DEFAULT_ALLOWED_CHARS\n const trimReplacement = options.trimReplacement ?? true\n const normalized = value.toLowerCase().trim()\n if (!normalized) return ''\n const escapedAllowed = escapeRegex(allowedChars)\n const invalidPattern = new RegExp(`[^a-z0-9${escapedAllowed}]+`, 'g')\n const replaced = normalized.replace(invalidPattern, replacement)\n if (!trimReplacement || replacement.length !== 1 || !replaced) return replaced\n const char = replacement\n let start = 0\n let end = replaced.length\n while (start < end && replaced[start] === char) start += 1\n while (end > start && replaced[end - 1] === char) end -= 1\n return replaced.slice(start, end)\n}\n"],
5
+ "mappings": "AAMA,MAAM,sBAAsB;AAC5B,MAAM,wBAAwB;AAE9B,MAAM,cAAc,CAAC,UAA0B,MAAM,QAAQ,yBAAyB,MAAM;AAErF,SAAS,QAAQ,OAAe,UAA0B,CAAC,GAAW;AAC3E,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAM,aAAa,MAAM,YAAY,EAAE,KAAK;AAC5C,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,iBAAiB,YAAY,YAAY;AAC/C,QAAM,iBAAiB,IAAI,OAAO,WAAW,cAAc,MAAM,GAAG;AACpE,QAAM,WAAW,WAAW,QAAQ,gBAAgB,WAAW;AAC/D,MAAI,CAAC,mBAAmB,YAAY,WAAW,KAAK,CAAC,SAAU,QAAO;AACtE,QAAM,OAAO;AACb,MAAI,QAAQ;AACZ,MAAI,MAAM,SAAS;AACnB,SAAO,QAAQ,OAAO,SAAS,KAAK,MAAM,KAAM,UAAS;AACzD,SAAO,MAAM,SAAS,SAAS,MAAM,CAAC,MAAM,KAAM,QAAO;AACzD,SAAO,SAAS,MAAM,OAAO,GAAG;AAClC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,51 @@
1
+ let _testBootstrapped = false;
2
+ async function bootstrapTest(options = {}) {
3
+ const { modules, entityIds, ormEntities, diRegistrars } = options;
4
+ if (modules !== void 0) {
5
+ const { registerModules } = await import("../modules/registry.js");
6
+ registerModules(modules);
7
+ }
8
+ if (entityIds !== void 0) {
9
+ const { registerEntityIds } = await import("../encryption/entityIds.js");
10
+ registerEntityIds(entityIds);
11
+ }
12
+ if (ormEntities !== void 0) {
13
+ const { registerOrmEntities } = await import("../db/mikro.js");
14
+ registerOrmEntities(ormEntities);
15
+ }
16
+ if (diRegistrars !== void 0) {
17
+ const { registerDiRegistrars } = await import("../di/container.js");
18
+ registerDiRegistrars(diRegistrars);
19
+ }
20
+ _testBootstrapped = true;
21
+ }
22
+ function resetTestBootstrap() {
23
+ _testBootstrapped = false;
24
+ }
25
+ function isTestBootstrapped() {
26
+ return _testBootstrapped;
27
+ }
28
+ function createMockModules(overrides = []) {
29
+ return overrides.map((override, index) => ({
30
+ id: override.id || `test-module-${index}`,
31
+ ...override
32
+ }));
33
+ }
34
+ function createMockEntityIds(entities) {
35
+ const result = {};
36
+ for (const [module, entityNames] of Object.entries(entities)) {
37
+ result[module] = {};
38
+ for (const name of entityNames) {
39
+ result[module][name] = `${module}:${name}`;
40
+ }
41
+ }
42
+ return result;
43
+ }
44
+ export {
45
+ bootstrapTest,
46
+ createMockEntityIds,
47
+ createMockModules,
48
+ isTestBootstrapped,
49
+ resetTestBootstrap
50
+ };
51
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/testing/bootstrap.ts"],
4
+ "sourcesContent": ["/**\n * Test Bootstrap Utility\n *\n * Provides a centralized way to bootstrap dependencies for tests.\n * This utility allows tests to register only the dependencies they need\n * without importing the full app bootstrap.\n *\n * Usage in tests:\n *\n * ```typescript\n * import { bootstrapTest, resetTestBootstrap } from '@open-mercato/shared/lib/testing/bootstrap'\n *\n * beforeEach(async () => {\n * resetTestBootstrap()\n * await bootstrapTest({\n * modules: mockModules,\n * entityIds: mockEntityIds,\n * })\n * })\n * ```\n *\n * For tests that use jest.resetModules(), import dynamically:\n *\n * ```typescript\n * beforeEach(async () => {\n * jest.resetModules()\n * const { registerModules } = await import('@open-mercato/shared/modules/registry')\n * registerModules(mockModules as any)\n * })\n * ```\n */\n\nimport type { Module } from '../../modules/registry'\n\nexport type EntityIds = Record<string, Record<string, string>>\n\nexport interface TestBootstrapOptions {\n /** Modules to register (for i18n, query engine, etc.) */\n modules?: Module[]\n /** Entity IDs to register (for encryption, indexing) */\n entityIds?: EntityIds\n /** ORM entities to register (rarely needed in unit tests) */\n ormEntities?: any[]\n /** DI registrars to register (rarely needed in unit tests) */\n diRegistrars?: Array<(container: any) => void>\n}\n\nlet _testBootstrapped = false\n\n/**\n * Bootstrap dependencies for tests.\n * Call this in beforeEach or beforeAll to set up required registrations.\n */\nexport async function bootstrapTest(options: TestBootstrapOptions = {}): Promise<void> {\n const { modules, entityIds, ormEntities, diRegistrars } = options\n\n if (modules !== undefined) {\n // Import lazily to avoid circular dependencies\n const { registerModules } = await import('../modules/registry.js')\n registerModules(modules)\n }\n\n if (entityIds !== undefined) {\n const { registerEntityIds } = await import('../encryption/entityIds.js')\n registerEntityIds(entityIds)\n }\n\n if (ormEntities !== undefined) {\n const { registerOrmEntities } = await import('../db/mikro.js')\n registerOrmEntities(ormEntities)\n }\n\n if (diRegistrars !== undefined) {\n const { registerDiRegistrars } = await import('../di/container.js')\n registerDiRegistrars(diRegistrars)\n }\n\n _testBootstrapped = true\n}\n\n/**\n * Reset the test bootstrap state.\n * Call this in beforeEach when you need fresh state between tests.\n *\n * Note: This only resets the test bootstrap flag. To fully reset\n * registration state, you may need to use jest.resetModules() and\n * re-import the registration functions.\n */\nexport function resetTestBootstrap(): void {\n _testBootstrapped = false\n}\n\n/**\n * Check if test bootstrap has been called.\n */\nexport function isTestBootstrapped(): boolean {\n return _testBootstrapped\n}\n\n/**\n * Helper to create minimal mock modules for testing.\n */\nexport function createMockModules(overrides: Partial<Module>[] = []): Module[] {\n return overrides.map((override, index) => ({\n id: override.id || `test-module-${index}`,\n ...override,\n })) as Module[]\n}\n\n/**\n * Helper to create minimal mock entity IDs for testing.\n */\nexport function createMockEntityIds(\n entities: Record<string, string[]>\n): EntityIds {\n const result: EntityIds = {}\n for (const [module, entityNames] of Object.entries(entities)) {\n result[module] = {}\n for (const name of entityNames) {\n result[module][name] = `${module}:${name}`\n }\n }\n return result\n}\n"],
5
+ "mappings": "AA+CA,IAAI,oBAAoB;AAMxB,eAAsB,cAAc,UAAgC,CAAC,GAAkB;AACrF,QAAM,EAAE,SAAS,WAAW,aAAa,aAAa,IAAI;AAE1D,MAAI,YAAY,QAAW;AAEzB,UAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,wBAAwB;AACjE,oBAAgB,OAAO;AAAA,EACzB;AAEA,MAAI,cAAc,QAAW;AAC3B,UAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,4BAA4B;AACvE,sBAAkB,SAAS;AAAA,EAC7B;AAEA,MAAI,gBAAgB,QAAW;AAC7B,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,gBAAgB;AAC7D,wBAAoB,WAAW;AAAA,EACjC;AAEA,MAAI,iBAAiB,QAAW;AAC9B,UAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,oBAAoB;AAClE,yBAAqB,YAAY;AAAA,EACnC;AAEA,sBAAoB;AACtB;AAUO,SAAS,qBAA2B;AACzC,sBAAoB;AACtB;AAKO,SAAS,qBAA8B;AAC5C,SAAO;AACT;AAKO,SAAS,kBAAkB,YAA+B,CAAC,GAAa;AAC7E,SAAO,UAAU,IAAI,CAAC,UAAU,WAAW;AAAA,IACzC,IAAI,SAAS,MAAM,eAAe,KAAK;AAAA,IACvC,GAAG;AAAA,EACL,EAAE;AACJ;AAKO,SAAS,oBACd,UACW;AACX,QAAM,SAAoB,CAAC;AAC3B,aAAW,CAAC,QAAQ,WAAW,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC5D,WAAO,MAAM,IAAI,CAAC;AAClB,eAAW,QAAQ,aAAa;AAC9B,aAAO,MAAM,EAAE,IAAI,IAAI,GAAG,MAAM,IAAI,IAAI;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ import {
2
+ bootstrapTest,
3
+ resetTestBootstrap,
4
+ isTestBootstrapped,
5
+ createMockModules,
6
+ createMockEntityIds
7
+ } from "./bootstrap.js";
8
+ import { renderWithProviders } from "./renderWithProviders.js";
9
+ export {
10
+ bootstrapTest,
11
+ createMockEntityIds,
12
+ createMockModules,
13
+ isTestBootstrapped,
14
+ renderWithProviders,
15
+ resetTestBootstrap
16
+ };
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/testing/index.ts"],
4
+ "sourcesContent": ["/**\n * Testing utilities for @open-mercato packages\n */\n\nexport {\n bootstrapTest,\n resetTestBootstrap,\n isTestBootstrapped,\n createMockModules,\n createMockEntityIds,\n type TestBootstrapOptions,\n type EntityIds,\n} from './bootstrap'\n\nexport { renderWithProviders } from './renderWithProviders'\n"],
5
+ "mappings": "AAIA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,2BAA2B;",
6
+ "names": []
7
+ }