@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,325 @@
1
+ import type { EntityId } from './entities'
2
+
3
+ // =============================================================================
4
+ // Strategy Identifiers
5
+ // =============================================================================
6
+
7
+ /**
8
+ * Built-in strategy identifiers plus extensible string for third-party strategies.
9
+ */
10
+ export type SearchStrategyId = 'tokens' | 'vector' | 'fulltext' | (string & {})
11
+
12
+ // =============================================================================
13
+ // Result Types
14
+ // =============================================================================
15
+
16
+ /**
17
+ * Presenter metadata for displaying search results in UI (Cmd+K, global search).
18
+ */
19
+ export type SearchResultPresenter = {
20
+ title: string
21
+ subtitle?: string
22
+ icon?: string
23
+ badge?: string
24
+ }
25
+
26
+ /**
27
+ * Deep link rendered next to a search result.
28
+ */
29
+ export type SearchResultLink = {
30
+ href: string
31
+ label: string
32
+ kind?: 'primary' | 'secondary'
33
+ }
34
+
35
+ /**
36
+ * A single search result returned by a strategy.
37
+ */
38
+ export type SearchResult = {
39
+ /** Entity type identifier, e.g., 'customers:customer_person_profile' */
40
+ entityId: EntityId
41
+ /** Record primary key */
42
+ recordId: string
43
+ /** Relevance score (normalized 0-1 range preferred, but RRF scores may exceed 1) */
44
+ score: number
45
+ /** Which strategy produced this result */
46
+ source: SearchStrategyId
47
+ /** Optional presenter for quick display */
48
+ presenter?: SearchResultPresenter
49
+ /** Primary URL when result is clicked */
50
+ url?: string
51
+ /** Additional action links */
52
+ links?: SearchResultLink[]
53
+ /** Extra metadata from the strategy */
54
+ metadata?: Record<string, unknown>
55
+ }
56
+
57
+ // =============================================================================
58
+ // Search Options
59
+ // =============================================================================
60
+
61
+ /**
62
+ * Options passed to SearchService.search()
63
+ */
64
+ export type SearchOptions = {
65
+ /** Tenant isolation - required */
66
+ tenantId: string
67
+ /** Optional organization filter */
68
+ organizationId?: string | null
69
+ /** Filter to specific entity types */
70
+ entityTypes?: EntityId[]
71
+ /** Use only specific strategies (defaults to all available) */
72
+ strategies?: SearchStrategyId[]
73
+ /** Maximum results per strategy before merging */
74
+ limit?: number
75
+ /** Offset for pagination */
76
+ offset?: number
77
+ /** How to combine results: 'or' merges all, 'and' requires match in all strategies */
78
+ combineMode?: 'or' | 'and'
79
+ }
80
+
81
+ // =============================================================================
82
+ // Indexable Record
83
+ // =============================================================================
84
+
85
+ /**
86
+ * A record prepared for indexing across all strategies.
87
+ */
88
+ export type IndexableRecord = {
89
+ /** Entity type identifier */
90
+ entityId: EntityId
91
+ /** Record primary key */
92
+ recordId: string
93
+ /** Tenant for isolation */
94
+ tenantId: string
95
+ /** Optional organization for additional filtering */
96
+ organizationId?: string | null
97
+ /** All fields from the record (strategies will filter based on their needs) */
98
+ fields: Record<string, unknown>
99
+ /** Optional presenter for result display */
100
+ presenter?: SearchResultPresenter
101
+ /** Primary URL for the record */
102
+ url?: string
103
+ /** Additional action links */
104
+ links?: SearchResultLink[]
105
+ /** Text content for embedding (from buildSource, used by vector strategy) */
106
+ text?: string | string[]
107
+ /** Source object for checksum calculation (change detection) */
108
+ checksumSource?: unknown
109
+ }
110
+
111
+ // =============================================================================
112
+ // Strategy Interface
113
+ // =============================================================================
114
+
115
+ /**
116
+ * Interface that all search strategies must implement.
117
+ * Following the cache module's strategy pattern.
118
+ */
119
+ export interface SearchStrategy {
120
+ /** Unique strategy identifier */
121
+ readonly id: SearchStrategyId
122
+
123
+ /** Human-readable name for debugging/logging */
124
+ readonly name: string
125
+
126
+ /** Priority for result merging (higher = more prominent in results) */
127
+ readonly priority: number
128
+
129
+ /** Check if strategy is available and configured */
130
+ isAvailable(): Promise<boolean>
131
+
132
+ /** Initialize strategy resources (lazy, called on first use) */
133
+ ensureReady(): Promise<void>
134
+
135
+ /** Execute a search query */
136
+ search(query: string, options: SearchOptions): Promise<SearchResult[]>
137
+
138
+ /** Index a record */
139
+ index(record: IndexableRecord): Promise<void>
140
+
141
+ /** Delete a record from the index */
142
+ delete(entityId: EntityId, recordId: string, tenantId: string): Promise<void>
143
+
144
+ /** Bulk index multiple records (optional optimization) */
145
+ bulkIndex?(records: IndexableRecord[]): Promise<void>
146
+
147
+ /** Purge all records for an entity type (optional) */
148
+ purge?(entityId: EntityId, tenantId: string): Promise<void>
149
+ }
150
+
151
+ // =============================================================================
152
+ // Service Configuration
153
+ // =============================================================================
154
+
155
+ /**
156
+ * Configuration for result merging across strategies.
157
+ */
158
+ export type ResultMergeConfig = {
159
+ /** How to handle duplicate results: 'highest_score' | 'first' | 'merge_scores' */
160
+ duplicateHandling: 'highest_score' | 'first' | 'merge_scores'
161
+ /** Weight multipliers per strategy (e.g., { meilisearch: 1.2, tokens: 0.8 }) */
162
+ strategyWeights?: Record<SearchStrategyId, number>
163
+ /** Minimum score threshold to include in results */
164
+ minScore?: number
165
+ }
166
+
167
+ /**
168
+ * Callback function to enrich search results with presenter data.
169
+ * Used to load presenter from database when not available from search strategy.
170
+ */
171
+ export type PresenterEnricherFn = (
172
+ results: SearchResult[],
173
+ tenantId: string,
174
+ organizationId?: string | null,
175
+ ) => Promise<SearchResult[]>
176
+
177
+ /**
178
+ * Options for creating a SearchService instance.
179
+ */
180
+ export type SearchServiceOptions = {
181
+ /** Array of strategy instances */
182
+ strategies?: SearchStrategy[]
183
+ /** Default strategies to use when not specified in search options */
184
+ defaultStrategies?: SearchStrategyId[]
185
+ /** Fallback strategy when others fail */
186
+ fallbackStrategy?: SearchStrategyId
187
+ /** Configuration for merging results from multiple strategies */
188
+ mergeConfig?: ResultMergeConfig
189
+ /** Callback to enrich results with presenter data from database */
190
+ presenterEnricher?: PresenterEnricherFn
191
+ }
192
+
193
+ // =============================================================================
194
+ // Module Configuration (for modules defining searchable entities)
195
+ // =============================================================================
196
+
197
+ /**
198
+ * Context passed to buildSource, formatResult, resolveUrl, and resolveLinks.
199
+ */
200
+ export type SearchBuildContext = {
201
+ /** The record being indexed */
202
+ record: Record<string, unknown>
203
+ /** Custom fields for the record */
204
+ customFields: Record<string, unknown>
205
+ /** Organization ID if applicable */
206
+ organizationId?: string | null
207
+ /** Tenant ID */
208
+ tenantId?: string | null
209
+ /** DI container for resolving dependencies */
210
+ container?: unknown
211
+ /** Query engine for loading related records (optional, used by buildSource for entity hydration) */
212
+ queryEngine?: unknown
213
+ }
214
+
215
+ /**
216
+ * Source data for indexing a record.
217
+ */
218
+ export type SearchIndexSource = {
219
+ /** Text content for keyword/fuzzy search (single string or array of chunks) */
220
+ text: string | string[]
221
+ /** Optional structured fields for filtering */
222
+ fields?: Record<string, unknown>
223
+ /** Presenter for quick display in search results */
224
+ presenter?: SearchResultPresenter
225
+ /** Deep links for the result */
226
+ links?: SearchResultLink[]
227
+ /** Source object used for checksum calculation (change detection) */
228
+ checksumSource?: unknown
229
+ }
230
+
231
+ /**
232
+ * Policy defining how fields should be handled for search indexing.
233
+ */
234
+ export type SearchFieldPolicy = {
235
+ /** Fields safe to send to external providers (fuzzy searchable) */
236
+ searchable?: string[]
237
+ /** Fields for hash-based search only (encrypted/sensitive) */
238
+ hashOnly?: string[]
239
+ /** Fields to exclude from all search */
240
+ excluded?: string[]
241
+ }
242
+
243
+ /**
244
+ * Configuration for a single searchable entity within a module.
245
+ */
246
+ export type SearchEntityConfig = {
247
+ /** Entity identifier, e.g., 'customers:customer_person_profile' */
248
+ entityId: EntityId
249
+ /** Enable/disable search for this entity (default: true) */
250
+ enabled?: boolean
251
+ /** Override strategies for this specific entity */
252
+ strategies?: SearchStrategyId[]
253
+ /** Priority for result ordering (higher = more prominent) */
254
+ priority?: number
255
+ /** Build searchable content from record */
256
+ buildSource?: (ctx: SearchBuildContext) => Promise<SearchIndexSource | null> | SearchIndexSource | null
257
+ /** Format result for display in Cmd+K */
258
+ formatResult?: (ctx: SearchBuildContext) => Promise<SearchResultPresenter | null> | SearchResultPresenter | null
259
+ /** Resolve primary URL when result is clicked */
260
+ resolveUrl?: (ctx: SearchBuildContext) => Promise<string | null> | string | null
261
+ /** Resolve additional action links */
262
+ resolveLinks?: (ctx: SearchBuildContext) => Promise<SearchResultLink[] | null> | SearchResultLink[] | null
263
+ /** Define which fields are searchable vs hash-only */
264
+ fieldPolicy?: SearchFieldPolicy
265
+ }
266
+
267
+ /**
268
+ * Module-level search configuration (defined in search.ts files).
269
+ */
270
+ export type SearchModuleConfig = {
271
+ /** Default strategies for all entities in this module */
272
+ defaultStrategies?: SearchStrategyId[]
273
+ /** Entity configurations */
274
+ entities: SearchEntityConfig[]
275
+ }
276
+
277
+ // =============================================================================
278
+ // Event Payloads (for indexer events)
279
+ // =============================================================================
280
+
281
+ /**
282
+ * Payload for search.index_record events.
283
+ */
284
+ export type SearchIndexPayload = {
285
+ entityId: EntityId
286
+ recordId: string
287
+ tenantId: string
288
+ organizationId?: string | null
289
+ record: Record<string, unknown>
290
+ customFields?: Record<string, unknown>
291
+ }
292
+
293
+ /**
294
+ * Payload for search.delete_record events.
295
+ */
296
+ export type SearchDeletePayload = {
297
+ entityId: EntityId
298
+ recordId: string
299
+ tenantId: string
300
+ }
301
+
302
+ // =============================================================================
303
+ // Global Registry for Search Module Configs
304
+ // =============================================================================
305
+
306
+ let _searchModuleConfigs: SearchModuleConfig[] | null = null
307
+
308
+ /**
309
+ * Register search module configurations globally.
310
+ * Called during app bootstrap with configs from search.generated.ts.
311
+ */
312
+ export function registerSearchModuleConfigs(configs: SearchModuleConfig[]): void {
313
+ if (_searchModuleConfigs !== null && process.env.NODE_ENV === 'development') {
314
+ console.debug('[Bootstrap] Search module configs re-registered (this may occur during HMR)')
315
+ }
316
+ _searchModuleConfigs = configs
317
+ }
318
+
319
+ /**
320
+ * Get registered search module configurations.
321
+ * Returns empty array if not registered (search module may not be enabled).
322
+ */
323
+ export function getSearchModuleConfigs(): SearchModuleConfig[] {
324
+ return _searchModuleConfigs ?? []
325
+ }
@@ -0,0 +1,122 @@
1
+ import type { EntityId } from './entities'
2
+ import type { QueryEngine } from '../lib/query/types'
3
+
4
+ export type VectorDriverId = 'pgvector' | 'qdrant' | 'chromadb'
5
+
6
+ export type VectorLinkDescriptor = {
7
+ href: string
8
+ label?: string
9
+ icon?: string
10
+ kind?: 'primary' | 'secondary'
11
+ }
12
+
13
+ export type VectorResultPresenter = {
14
+ title: string
15
+ subtitle?: string
16
+ icon?: string
17
+ badge?: string
18
+ }
19
+
20
+ export type VectorIndexSource = {
21
+ /**
22
+ * Text or text chunks passed to the embedding model. Provide multiple chunks for larger payloads.
23
+ */
24
+ input: string | string[]
25
+ /**
26
+ * Optional metadata persisted alongside the embedding for quick display.
27
+ */
28
+ presenter?: VectorResultPresenter | null
29
+ /**
30
+ * Optional deep links rendered next to the result.
31
+ */
32
+ links?: VectorLinkDescriptor[] | null
33
+ /**
34
+ * Additional payload mirrored into the driver metadata column (JSONB).
35
+ */
36
+ payload?: Record<string, unknown> | null
37
+ /**
38
+ * Source object used when computing a checksum. Defaults to the combination of record and custom fields.
39
+ */
40
+ checksumSource?: unknown
41
+ }
42
+
43
+ export type VectorBuildContext = {
44
+ record: Record<string, any>
45
+ customFields: Record<string, any>
46
+ organizationId?: string | null
47
+ tenantId?: string | null
48
+ queryEngine?: QueryEngine
49
+ container?: unknown
50
+ }
51
+
52
+ export type VectorEntityConfig = {
53
+ entityId: EntityId
54
+ enabled?: boolean
55
+ driverId?: VectorDriverId
56
+ priority?: number
57
+ /**
58
+ * Optional builder that returns the string payload to embed plus supplemental metadata.
59
+ * When omitted the service will stringify the record (including custom fields).
60
+ */
61
+ buildSource?: (ctx: VectorBuildContext) => Promise<VectorIndexSource | null> | VectorIndexSource | null
62
+ /**
63
+ * Resolve the primary admin URL for this record. When omitted the service expects a link inside `links`.
64
+ */
65
+ resolveUrl?: (ctx: VectorBuildContext) => Promise<string | null> | string | null
66
+ /**
67
+ * Format the presenter displayed inside global search overlays.
68
+ */
69
+ formatResult?: (ctx: VectorBuildContext) => Promise<VectorResultPresenter | null> | VectorResultPresenter | null
70
+ /**
71
+ * Provide extra deep links rendered next to the search result.
72
+ */
73
+ resolveLinks?: (ctx: VectorBuildContext) => Promise<VectorLinkDescriptor[] | null> | VectorLinkDescriptor[] | null
74
+ }
75
+
76
+ export type VectorModuleConfig = {
77
+ defaultDriverId?: VectorDriverId
78
+ entities: VectorEntityConfig[]
79
+ }
80
+
81
+ export type VectorQueryRequest = {
82
+ query: string
83
+ tenantId: string
84
+ organizationId?: string | null
85
+ limit?: number
86
+ driverId?: VectorDriverId
87
+ }
88
+
89
+ export type VectorSearchHit = {
90
+ entityId: EntityId
91
+ recordId: string
92
+ score: number
93
+ url?: string | null
94
+ presenter?: VectorResultPresenter | null
95
+ links?: VectorLinkDescriptor[] | null
96
+ driverId: VectorDriverId
97
+ metadata?: Record<string, unknown> | null
98
+ }
99
+
100
+ export type VectorIndexEntry = {
101
+ entityId: EntityId
102
+ recordId: string
103
+ driverId: VectorDriverId
104
+ tenantId: string
105
+ organizationId?: string | null
106
+ checksum: string
107
+ url?: string | null
108
+ presenter?: VectorResultPresenter | null
109
+ links?: VectorLinkDescriptor[] | null
110
+ payload?: Record<string, unknown> | null
111
+ metadata?: Record<string, unknown> | null
112
+ resultTitle: string
113
+ resultSubtitle?: string | null
114
+ resultIcon?: string | null
115
+ resultBadge?: string | null
116
+ resultSnapshot?: string | null
117
+ primaryLinkHref?: string | null
118
+ primaryLinkLabel?: string | null
119
+ createdAt: string
120
+ updatedAt: string
121
+ score?: number | null
122
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ import { describe, it, expect } from '@jest/globals'
5
+ import type { InjectionWidgetMetadata, InjectionWidgetModule, ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'
6
+
7
+ describe('Widget Injection Types', () => {
8
+ it('should allow defining widget metadata', () => {
9
+ const metadata: InjectionWidgetMetadata = {
10
+ id: 'test.widget',
11
+ title: 'Test Widget',
12
+ description: 'A test widget',
13
+ features: ['test.feature'],
14
+ priority: 100,
15
+ enabled: true,
16
+ }
17
+ expect(metadata.id).toBe('test.widget')
18
+ })
19
+
20
+ it('should allow defining injection table', () => {
21
+ const table: ModuleInjectionTable = {
22
+ 'crud-form:test': 'test.widget',
23
+ 'crud-form:test2': ['widget1', 'widget2'],
24
+ 'crud-form:grouped': [{ widgetId: 'widget3', kind: 'group', groupLabel: 'Extra fields' }],
25
+ }
26
+ expect(table['crud-form:test']).toBe('test.widget')
27
+ expect(Array.isArray(table['crud-form:test2'])).toBe(true)
28
+ const grouped = table['crud-form:grouped'] as any[]
29
+ expect(grouped[0].groupLabel).toBe('Extra fields')
30
+ })
31
+
32
+ it('should type event handlers correctly', () => {
33
+ const widget: Partial<InjectionWidgetModule<any, any>> = {
34
+ eventHandlers: {
35
+ onLoad: async (context) => {
36
+ expect(context).toBeDefined()
37
+ },
38
+ onBeforeSave: async (data, context) => {
39
+ expect(data).toBeDefined()
40
+ expect(context).toBeDefined()
41
+ return true
42
+ },
43
+ },
44
+ }
45
+ expect(widget.eventHandlers?.onLoad).toBeDefined()
46
+ expect(widget.eventHandlers?.onBeforeSave).toBeDefined()
47
+ })
48
+ })