@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,235 @@
1
+ import type { ModuleInjectionWidgetEntry } from '../registry'
2
+ import type {
3
+ InjectionWidgetMetadata,
4
+ InjectionWidgetModule,
5
+ InjectionSpotId,
6
+ ModuleInjectionSlot,
7
+ ModuleInjectionTable,
8
+ InjectionWidgetPlacement,
9
+ } from './injection'
10
+
11
+ type LoadedWidgetModule = InjectionWidgetModule<any, any> & { metadata: InjectionWidgetMetadata }
12
+ export type LoadedInjectionWidget = LoadedWidgetModule & {
13
+ moduleId: string
14
+ key: string
15
+ placement?: {
16
+ groupId?: string
17
+ groupLabel?: string
18
+ groupDescription?: string
19
+ column?: 1 | 2
20
+ kind?: 'tab' | 'group' | 'stack'
21
+ [k: string]: unknown
22
+ }
23
+ }
24
+
25
+ type WidgetEntry = ModuleInjectionWidgetEntry & { moduleId: string }
26
+
27
+ // Registration pattern for publishable packages
28
+ let _coreInjectionWidgetEntries: ModuleInjectionWidgetEntry[] | null = null
29
+ let _coreInjectionTables: Array<{ moduleId: string; table: ModuleInjectionTable }> | null = null
30
+
31
+ export function registerCoreInjectionWidgets(entries: ModuleInjectionWidgetEntry[]) {
32
+ if (_coreInjectionWidgetEntries !== null && process.env.NODE_ENV === 'development') {
33
+ console.debug('[Bootstrap] Core injection widgets re-registered (this may occur during HMR)')
34
+ }
35
+ _coreInjectionWidgetEntries = entries
36
+ }
37
+
38
+ export function getCoreInjectionWidgets(): ModuleInjectionWidgetEntry[] {
39
+ if (!_coreInjectionWidgetEntries) {
40
+ // On client-side, bootstrap doesn't run - return empty array gracefully
41
+ if (typeof window !== 'undefined') {
42
+ return []
43
+ }
44
+ throw new Error('[Bootstrap] Core injection widgets not registered. Call registerCoreInjectionWidgets() at bootstrap.')
45
+ }
46
+ return _coreInjectionWidgetEntries
47
+ }
48
+
49
+ export function registerCoreInjectionTables(tables: Array<{ moduleId: string; table: ModuleInjectionTable }>) {
50
+ if (_coreInjectionTables !== null && process.env.NODE_ENV === 'development') {
51
+ console.debug('[Bootstrap] Core injection tables re-registered (this may occur during HMR)')
52
+ }
53
+ _coreInjectionTables = tables
54
+ }
55
+
56
+ export function getCoreInjectionTables(): Array<{ moduleId: string; table: ModuleInjectionTable }> {
57
+ if (!_coreInjectionTables) {
58
+ // On client-side, bootstrap doesn't run - return empty array gracefully
59
+ if (typeof window !== 'undefined') {
60
+ return []
61
+ }
62
+ throw new Error('[Bootstrap] Core injection tables not registered. Call registerCoreInjectionTables() at bootstrap.')
63
+ }
64
+ return _coreInjectionTables
65
+ }
66
+
67
+ let widgetEntriesPromise: Promise<WidgetEntry[]> | null = null
68
+ type TableEntry = {
69
+ widgetId: string
70
+ moduleId: string
71
+ priority: number
72
+ placement?: ModuleInjectionSlot extends infer S
73
+ ? S extends { widgetId: string }
74
+ ? Omit<S, 'widgetId' | 'priority'>
75
+ : never
76
+ : never
77
+ }
78
+ let injectionTablePromise: Promise<Map<InjectionSpotId, TableEntry[]>> | null = null
79
+
80
+ function isInjectionSlotObject(value: ModuleInjectionSlot): value is InjectionWidgetPlacement & { widgetId: string; priority?: number } {
81
+ return typeof value === 'object' && value !== null && 'widgetId' in value
82
+ }
83
+
84
+ /**
85
+ * Invalidate the widget entries and widget module cache.
86
+ * Call this when the generated registry is updated or modules are reloaded.
87
+ */
88
+ export function invalidateInjectionWidgetCache() {
89
+ widgetEntriesPromise = null
90
+ injectionTablePromise = null
91
+ widgetCache.clear()
92
+ }
93
+
94
+ async function loadWidgetEntries(): Promise<WidgetEntry[]> {
95
+ if (!widgetEntriesPromise) {
96
+ const promise = Promise.resolve().then(() =>
97
+ getCoreInjectionWidgets().map((entry) => ({
98
+ ...entry,
99
+ moduleId: entry.moduleId || 'unknown',
100
+ }))
101
+ )
102
+ widgetEntriesPromise = promise.catch((err) => {
103
+ // Clear cache on error so next call can retry after registration
104
+ if (widgetEntriesPromise === promise) {
105
+ widgetEntriesPromise = null
106
+ }
107
+ throw err
108
+ })
109
+ }
110
+ return widgetEntriesPromise
111
+ }
112
+
113
+ async function loadInjectionTable(): Promise<Map<InjectionSpotId, TableEntry[]>> {
114
+ if (!injectionTablePromise) {
115
+ const promise = Promise.resolve().then(() => {
116
+ const list = getCoreInjectionTables()
117
+ const table = new Map<InjectionSpotId, TableEntry[]>()
118
+
119
+ for (const entry of list) {
120
+ const injectionTable = entry.table ?? {}
121
+ for (const [spotId, widgetIds] of Object.entries(injectionTable)) {
122
+ const widgets = Array.isArray(widgetIds) ? widgetIds : [widgetIds]
123
+ const existing = table.get(spotId) ?? []
124
+ for (const widgetEntry of widgets) {
125
+ if (typeof widgetEntry === 'string') {
126
+ existing.push({ widgetId: widgetEntry, moduleId: entry.moduleId, priority: 0 })
127
+ continue
128
+ }
129
+ if (isInjectionSlotObject(widgetEntry)) {
130
+ const { widgetId, priority = 0, ...placement } = widgetEntry
131
+ existing.push({
132
+ widgetId,
133
+ moduleId: entry.moduleId,
134
+ priority: typeof priority === 'number' ? priority : 0,
135
+ placement,
136
+ })
137
+ continue
138
+ }
139
+ }
140
+ table.set(spotId, existing)
141
+ }
142
+ }
143
+
144
+ for (const [spotId, widgets] of table.entries()) {
145
+ table.set(spotId, widgets.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)))
146
+ }
147
+
148
+ return table
149
+ })
150
+ injectionTablePromise = promise.catch((err) => {
151
+ // Clear cache on error so next call can retry after registration
152
+ if (injectionTablePromise === promise) {
153
+ injectionTablePromise = null
154
+ }
155
+ throw err
156
+ })
157
+ }
158
+ return injectionTablePromise
159
+ }
160
+
161
+ const widgetCache = new Map<string, Promise<LoadedWidgetModule>>()
162
+
163
+ function ensureValidWidgetModule(mod: any, key: string, moduleId: string): LoadedWidgetModule {
164
+ if (!mod || typeof mod !== 'object') {
165
+ throw new Error(`Invalid injection widget module "${key}" from "${moduleId}" (expected object export)`)
166
+ }
167
+ const widget = (mod.default ?? mod) as InjectionWidgetModule<any, any>
168
+ if (!widget || typeof widget !== 'object') {
169
+ throw new Error(`Invalid injection widget export "${key}" from "${moduleId}" (missing default export)`)
170
+ }
171
+ if (!widget.metadata || typeof widget.metadata !== 'object') {
172
+ throw new Error(`Injection widget "${key}" from "${moduleId}" is missing metadata`)
173
+ }
174
+ const { metadata } = widget
175
+ if (typeof metadata.id !== 'string' || metadata.id.length === 0) {
176
+ throw new Error(`Injection widget "${key}" from "${moduleId}" metadata.id must be a non-empty string`)
177
+ }
178
+ if (typeof metadata.title !== 'string' || metadata.title.length === 0) {
179
+ throw new Error(`Injection widget "${metadata.id}" from "${moduleId}" must have a title`)
180
+ }
181
+ return {
182
+ ...widget,
183
+ metadata,
184
+ }
185
+ }
186
+
187
+ async function loadEntry(entry: WidgetEntry): Promise<LoadedWidgetModule> {
188
+ if (!widgetCache.has(entry.key)) {
189
+ const promise = entry.loader()
190
+ .then((mod) => ensureValidWidgetModule(mod, entry.key, entry.moduleId))
191
+ widgetCache.set(entry.key, promise)
192
+ }
193
+ return widgetCache.get(entry.key)!
194
+ }
195
+
196
+ export async function loadAllInjectionWidgets(): Promise<LoadedInjectionWidget[]> {
197
+ const widgetEntries = await loadWidgetEntries()
198
+ const loaded = await Promise.all(widgetEntries.map(async (entry) => {
199
+ const widget = await loadEntry(entry)
200
+ return { ...widget, moduleId: entry.moduleId, key: entry.key }
201
+ }))
202
+ const byId = new Map<string, LoadedWidgetModule & { moduleId: string; key: string }>()
203
+ for (const widget of loaded) {
204
+ if (!byId.has(widget.metadata.id)) {
205
+ byId.set(widget.metadata.id, widget)
206
+ }
207
+ }
208
+ return Array.from(byId.values())
209
+ }
210
+
211
+ export async function loadInjectionWidgetById(widgetId: string): Promise<LoadedInjectionWidget | null> {
212
+ const widgetEntries = await loadWidgetEntries()
213
+ for (const entry of widgetEntries) {
214
+ const widget = await loadEntry(entry)
215
+ if (widget.metadata.id === widgetId) {
216
+ return { ...widget, moduleId: entry.moduleId, key: entry.key }
217
+ }
218
+ }
219
+ return null
220
+ }
221
+
222
+ export async function loadInjectionWidgetsForSpot(spotId: InjectionSpotId): Promise<LoadedInjectionWidget[]> {
223
+ const table = await loadInjectionTable()
224
+ const entries = table.get(spotId) ?? []
225
+ const widgets = await Promise.all(
226
+ entries.map(async ({ widgetId, placement, priority }) => {
227
+ const widget = await loadInjectionWidgetById(widgetId)
228
+ const combinedPlacement = placement
229
+ ? { ...placement, priority: typeof priority === 'number' ? priority : 0 }
230
+ : { priority: typeof priority === 'number' ? priority : 0 }
231
+ return widget ? { ...widget, placement: combinedPlacement } : null
232
+ })
233
+ )
234
+ return widgets.filter((w): w is NonNullable<typeof w> => w !== null)
235
+ }
@@ -0,0 +1,120 @@
1
+ import type { ComponentType } from 'react'
2
+
3
+ /**
4
+ * Widget injection event handlers for lifecycle management
5
+ */
6
+ export type WidgetInjectionEventHandlers<TContext = unknown, TData = unknown> = {
7
+ /**
8
+ * Called when the widget is first loaded/mounted
9
+ */
10
+ onLoad?: (context: TContext) => void | Promise<void>
11
+
12
+ /**
13
+ * Called before save action is executed
14
+ * Return false or throw to prevent save. Optionally return an object with
15
+ * `ok`, `message`, and `fieldErrors` to surface a user-facing reason.
16
+ */
17
+ onBeforeSave?: (data: TData, context: TContext) => WidgetBeforeSaveResult | Promise<WidgetBeforeSaveResult>
18
+
19
+ /**
20
+ * Called when save action is triggered
21
+ */
22
+ onSave?: (data: TData, context: TContext) => void | Promise<void>
23
+
24
+ /**
25
+ * Called after save action completes successfully
26
+ */
27
+ onAfterSave?: (data: TData, context: TContext) => void | Promise<void>
28
+ }
29
+
30
+ /**
31
+ * Metadata for an injection widget
32
+ */
33
+ export type InjectionWidgetMetadata = {
34
+ id: string
35
+ title: string
36
+ description?: string
37
+ features?: string[]
38
+ priority?: number
39
+ enabled?: boolean
40
+ }
41
+
42
+ /**
43
+ * Optional placement metadata declared per injection spot entry.
44
+ * Lets hosts render widgets in tabs, grouped cards, or plain stacks.
45
+ */
46
+ export type InjectionWidgetPlacement = {
47
+ /**
48
+ * Optional stable identifier for grouping (e.g., tab id or card id)
49
+ */
50
+ groupId?: string
51
+ /**
52
+ * Display label for the group or tab. Falls back to widget title.
53
+ */
54
+ groupLabel?: string
55
+ /**
56
+ * Optional helper text shown with the widget when rendered as a group.
57
+ */
58
+ groupDescription?: string
59
+ /**
60
+ * Preferred column for grouped layouts (1 = left, 2 = right)
61
+ */
62
+ column?: 1 | 2
63
+ /**
64
+ * Rendering hint for hosts that support categorized widgets.
65
+ * - 'tab' → render as a tab with the widget as tab content
66
+ * - 'group' → render inside a grouped card/section
67
+ * - 'stack' → render inline (default)
68
+ */
69
+ kind?: 'tab' | 'group' | 'stack'
70
+ }
71
+
72
+ /**
73
+ * Props passed to injection widget components
74
+ */
75
+ export type InjectionWidgetComponentProps<TContext = unknown, TData = unknown> = {
76
+ context: TContext
77
+ data?: TData
78
+ onDataChange?: (data: TData) => void
79
+ disabled?: boolean
80
+ }
81
+
82
+ export type WidgetBeforeSaveResult =
83
+ | boolean
84
+ | void
85
+ | {
86
+ ok?: boolean
87
+ message?: string
88
+ fieldErrors?: Record<string, string>
89
+ }
90
+
91
+ /**
92
+ * Complete injection widget module definition
93
+ */
94
+ export type InjectionWidgetModule<TContext = unknown, TData = unknown> = {
95
+ metadata: InjectionWidgetMetadata
96
+ Widget: ComponentType<InjectionWidgetComponentProps<TContext, TData>>
97
+ eventHandlers?: WidgetInjectionEventHandlers<TContext, TData>
98
+ }
99
+
100
+ /**
101
+ * Injection spot identifier - uniquely identifies where widgets can be injected
102
+ */
103
+ export type InjectionSpotId = string
104
+
105
+ /**
106
+ * Injection table entry mapping spot to widget
107
+ */
108
+ export type InjectionTableEntry = {
109
+ spotId: InjectionSpotId
110
+ widgetId: string
111
+ priority?: number
112
+ placement?: InjectionWidgetPlacement
113
+ }
114
+
115
+ export type ModuleInjectionSlot = string | (InjectionWidgetPlacement & { widgetId: string; priority?: number })
116
+
117
+ /**
118
+ * Module's injection table - maps spots to widgets
119
+ */
120
+ export type ModuleInjectionTable = Record<InjectionSpotId, ModuleInjectionSlot | ModuleInjectionSlot[]>
@@ -0,0 +1,22 @@
1
+ export function matchFeature(required: string, granted: string): boolean {
2
+ if (granted === '*') return true
3
+ if (granted.endsWith('.*')) {
4
+ const prefix = granted.slice(0, -2)
5
+ return required === prefix || required.startsWith(prefix + '.')
6
+ }
7
+ return granted === required
8
+ }
9
+
10
+ export function hasFeature(granted: readonly string[] | undefined, required: string): boolean {
11
+ if (!Array.isArray(granted) || !granted.length) return false
12
+ return granted.some((feature) => matchFeature(required, feature))
13
+ }
14
+
15
+ export function hasAllFeatures(
16
+ granted: readonly string[] | undefined,
17
+ required: readonly string[] | undefined
18
+ ): boolean {
19
+ if (!required || required.length === 0) return true
20
+ if (!Array.isArray(granted) || !granted.length) return false
21
+ return required.every((feature) => hasFeature(granted, feature))
22
+ }
@@ -0,0 +1,2 @@
1
+ declare module 'pg';
2
+
@@ -0,0 +1,2 @@
1
+ declare module '@react-email/components';
2
+
@@ -0,0 +1,2 @@
1
+ declare module 'resend';
2
+
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "noEmit": false,
6
+ "declaration": false,
7
+ "outDir": "./dist",
8
+ "rootDir": "./src"
9
+ },
10
+ "include": ["src/**/*"]
11
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "../../tsconfig.base.json",
4
+ "compilerOptions": {
5
+ "noEmit": true
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist", "**/__tests__/**"]
9
+ }
package/watch.mjs ADDED
@@ -0,0 +1,6 @@
1
+ import { watch } from '../../scripts/watch.mjs'
2
+ import { dirname } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url))
6
+ watch(__dirname)