@open-mercato/core 0.4.2-canary-ed15f2e753 → 0.4.2-canary-f075c3eb92

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 (241) hide show
  1. package/dist/generated/entities.ids.generated.js +0 -1
  2. package/dist/generated/entities.ids.generated.js.map +2 -2
  3. package/dist/generated/entity-fields-registry.js +0 -2
  4. package/dist/generated/entity-fields-registry.js.map +2 -2
  5. package/dist/modules/api_keys/setup.js +11 -0
  6. package/dist/modules/api_keys/setup.js.map +7 -0
  7. package/dist/modules/attachments/components/AttachmentLibrary.js +1 -1
  8. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  9. package/dist/modules/attachments/lib/assignmentDetails.js +31 -17
  10. package/dist/modules/attachments/lib/assignmentDetails.js.map +2 -2
  11. package/dist/modules/attachments/lib/partitions.js +3 -3
  12. package/dist/modules/attachments/lib/partitions.js.map +2 -2
  13. package/dist/modules/attachments/setup.js +11 -0
  14. package/dist/modules/attachments/setup.js.map +7 -0
  15. package/dist/modules/audit_logs/setup.js +12 -0
  16. package/dist/modules/audit_logs/setup.js.map +7 -0
  17. package/dist/modules/auth/lib/setup-app.js +29 -159
  18. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  19. package/dist/modules/auth/setup.js +11 -0
  20. package/dist/modules/auth/setup.js.map +7 -0
  21. package/dist/modules/business_rules/data/validators.js +0 -34
  22. package/dist/modules/business_rules/data/validators.js.map +2 -2
  23. package/dist/modules/business_rules/index.js +1 -21
  24. package/dist/modules/business_rules/index.js.map +2 -2
  25. package/dist/modules/business_rules/lib/rule-engine.js +1 -182
  26. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  27. package/dist/modules/business_rules/setup.js +11 -0
  28. package/dist/modules/business_rules/setup.js.map +7 -0
  29. package/dist/modules/catalog/setup.js +22 -0
  30. package/dist/modules/catalog/setup.js.map +7 -0
  31. package/dist/modules/configs/lib/upgrade-actions.js +65 -15
  32. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  33. package/dist/modules/configs/setup.js +16 -0
  34. package/dist/modules/configs/setup.js.map +7 -0
  35. package/dist/modules/currencies/setup.js +16 -0
  36. package/dist/modules/currencies/setup.js.map +7 -0
  37. package/dist/modules/customers/setup.js +36 -0
  38. package/dist/modules/customers/setup.js.map +7 -0
  39. package/dist/modules/dashboards/setup.js +12 -0
  40. package/dist/modules/dashboards/setup.js.map +7 -0
  41. package/dist/modules/dictionaries/setup.js +12 -0
  42. package/dist/modules/dictionaries/setup.js.map +7 -0
  43. package/dist/modules/directory/setup.js +12 -0
  44. package/dist/modules/directory/setup.js.map +7 -0
  45. package/dist/modules/entities/setup.js +11 -0
  46. package/dist/modules/entities/setup.js.map +7 -0
  47. package/dist/modules/feature_toggles/setup.js +11 -0
  48. package/dist/modules/feature_toggles/setup.js.map +7 -0
  49. package/dist/modules/perspectives/setup.js +12 -0
  50. package/dist/modules/perspectives/setup.js.map +7 -0
  51. package/dist/modules/planner/setup.js +21 -0
  52. package/dist/modules/planner/setup.js.map +7 -0
  53. package/dist/modules/query_index/setup.js +11 -0
  54. package/dist/modules/query_index/setup.js.map +7 -0
  55. package/dist/modules/resources/setup.js +21 -0
  56. package/dist/modules/resources/setup.js.map +7 -0
  57. package/dist/modules/sales/acl.js +0 -1
  58. package/dist/modules/sales/acl.js.map +2 -2
  59. package/dist/modules/sales/backend/sales/documents/[id]/page.js +0 -12
  60. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  61. package/dist/modules/sales/commands/documents.js +0 -62
  62. package/dist/modules/sales/commands/documents.js.map +2 -2
  63. package/dist/modules/sales/lib/dictionaries.js +0 -3
  64. package/dist/modules/sales/lib/dictionaries.js.map +2 -2
  65. package/dist/modules/sales/setup.js +99 -0
  66. package/dist/modules/sales/setup.js.map +7 -0
  67. package/dist/modules/staff/setup.js +27 -0
  68. package/dist/modules/staff/setup.js.map +7 -0
  69. package/dist/modules/workflows/acl.js +0 -2
  70. package/dist/modules/workflows/acl.js.map +2 -2
  71. package/dist/modules/workflows/api/instances/route.js +6 -18
  72. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  73. package/dist/modules/workflows/api/tasks/route.js +1 -6
  74. package/dist/modules/workflows/api/tasks/route.js.map +2 -2
  75. package/dist/modules/workflows/backend/definitions/[id]/page.js +1 -9
  76. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  77. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
  78. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
  79. package/dist/modules/workflows/backend/definitions/create/page.js +15 -24
  80. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  81. package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
  82. package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
  83. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +132 -150
  84. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  85. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
  86. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
  87. package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
  88. package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
  89. package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
  90. package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
  91. package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
  92. package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
  93. package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
  94. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  95. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
  96. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
  97. package/dist/modules/workflows/backend/tasks/page.js +6 -5
  98. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  99. package/dist/modules/workflows/cli.js +3 -81
  100. package/dist/modules/workflows/cli.js.map +3 -3
  101. package/dist/modules/workflows/data/entities.js +1 -64
  102. package/dist/modules/workflows/data/entities.js.map +2 -2
  103. package/dist/modules/workflows/data/validators.js +0 -115
  104. package/dist/modules/workflows/data/validators.js.map +2 -2
  105. package/dist/modules/workflows/examples/checkout-demo-definition.json +5 -1
  106. package/dist/modules/workflows/lib/activity-executor.js +13 -75
  107. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  108. package/dist/modules/workflows/lib/graph-utils.js +2 -71
  109. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  110. package/dist/modules/workflows/lib/seeds.js +7 -36
  111. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  112. package/dist/modules/workflows/lib/start-validator.js +23 -33
  113. package/dist/modules/workflows/lib/start-validator.js.map +2 -2
  114. package/dist/modules/workflows/lib/transition-handler.js +45 -157
  115. package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
  116. package/dist/modules/workflows/migrations/Migration20251207131955.js +76 -72
  117. package/dist/modules/workflows/migrations/Migration20251207131955.js.map +2 -2
  118. package/dist/modules/workflows/setup.js +16 -0
  119. package/dist/modules/workflows/setup.js.map +7 -0
  120. package/generated/entities.ids.generated.ts +0 -1
  121. package/generated/entity-fields-registry.ts +0 -2
  122. package/package.json +2 -2
  123. package/src/__tests__/module-decoupling.test.ts +356 -0
  124. package/src/modules/api_keys/setup.ts +9 -0
  125. package/src/modules/attachments/components/AttachmentLibrary.tsx +2 -2
  126. package/src/modules/attachments/lib/assignmentDetails.ts +32 -16
  127. package/src/modules/attachments/lib/partitions.ts +3 -3
  128. package/src/modules/attachments/setup.ts +9 -0
  129. package/src/modules/audit_logs/setup.ts +10 -0
  130. package/src/modules/auth/__tests__/cli-setup-acl.test.ts +30 -0
  131. package/src/modules/auth/lib/setup-app.ts +40 -177
  132. package/src/modules/auth/setup.ts +9 -0
  133. package/src/modules/business_rules/data/validators.ts +0 -40
  134. package/src/modules/business_rules/index.ts +0 -25
  135. package/src/modules/business_rules/lib/rule-engine.ts +1 -281
  136. package/src/modules/business_rules/setup.ts +9 -0
  137. package/src/modules/catalog/setup.ts +22 -0
  138. package/src/modules/configs/lib/upgrade-actions.ts +78 -17
  139. package/src/modules/configs/setup.ts +14 -0
  140. package/src/modules/currencies/setup.ts +15 -0
  141. package/src/modules/customers/setup.ts +36 -0
  142. package/src/modules/dashboards/setup.ts +10 -0
  143. package/src/modules/dictionaries/setup.ts +10 -0
  144. package/src/modules/directory/setup.ts +10 -0
  145. package/src/modules/entities/setup.ts +9 -0
  146. package/src/modules/feature_toggles/setup.ts +9 -0
  147. package/src/modules/perspectives/setup.ts +10 -0
  148. package/src/modules/planner/setup.ts +21 -0
  149. package/src/modules/query_index/setup.ts +9 -0
  150. package/src/modules/resources/setup.ts +21 -0
  151. package/src/modules/sales/acl.ts +0 -1
  152. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +0 -16
  153. package/src/modules/sales/commands/documents.ts +1 -74
  154. package/src/modules/sales/lib/dictionaries.ts +0 -3
  155. package/src/modules/sales/setup.ts +108 -0
  156. package/src/modules/staff/setup.ts +27 -0
  157. package/src/modules/workflows/acl.ts +0 -2
  158. package/src/modules/workflows/api/__tests__/instances.route.test.ts +2 -5
  159. package/src/modules/workflows/api/instances/route.ts +7 -21
  160. package/src/modules/workflows/api/tasks/route.ts +1 -7
  161. package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
  162. package/src/modules/workflows/backend/definitions/[id]/page.tsx +0 -9
  163. package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
  164. package/src/modules/workflows/backend/definitions/create/page.tsx +0 -9
  165. package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
  166. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3 -21
  167. package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
  168. package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
  169. package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
  170. package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
  171. package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
  172. package/src/modules/workflows/backend/tasks/page.tsx +6 -5
  173. package/src/modules/workflows/cli.ts +0 -111
  174. package/src/modules/workflows/data/entities.ts +0 -124
  175. package/src/modules/workflows/data/validators.ts +0 -138
  176. package/src/modules/workflows/examples/checkout-demo-definition.json +5 -1
  177. package/src/modules/workflows/i18n/en.json +0 -71
  178. package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +36 -43
  179. package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +90 -170
  180. package/src/modules/workflows/lib/activity-executor.ts +16 -129
  181. package/src/modules/workflows/lib/graph-utils.ts +2 -117
  182. package/src/modules/workflows/lib/seeds.ts +12 -50
  183. package/src/modules/workflows/lib/start-validator.ts +28 -38
  184. package/src/modules/workflows/lib/transition-handler.ts +55 -208
  185. package/src/modules/workflows/migrations/Migration20251207131955.ts +77 -143
  186. package/src/modules/workflows/setup.ts +15 -0
  187. package/dist/generated/entities/workflow_event_trigger/index.js +0 -33
  188. package/dist/generated/entities/workflow_event_trigger/index.js.map +0 -7
  189. package/dist/modules/auth/events.js +0 -30
  190. package/dist/modules/auth/events.js.map +0 -7
  191. package/dist/modules/business_rules/api/execute/[ruleId]/route.js +0 -145
  192. package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +0 -7
  193. package/dist/modules/catalog/events.js +0 -34
  194. package/dist/modules/catalog/events.js.map +0 -7
  195. package/dist/modules/customers/events.js +0 -49
  196. package/dist/modules/customers/events.js.map +0 -7
  197. package/dist/modules/directory/events.js +0 -23
  198. package/dist/modules/directory/events.js.map +0 -7
  199. package/dist/modules/sales/events.js +0 -63
  200. package/dist/modules/sales/events.js.map +0 -7
  201. package/dist/modules/sales/lib/frontend/documentDataEvents.js +0 -25
  202. package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +0 -7
  203. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +0 -481
  204. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +0 -7
  205. package/dist/modules/workflows/components/EventTriggersEditor.js +0 -553
  206. package/dist/modules/workflows/components/EventTriggersEditor.js.map +0 -7
  207. package/dist/modules/workflows/events.js +0 -38
  208. package/dist/modules/workflows/events.js.map +0 -7
  209. package/dist/modules/workflows/examples/order-approval-definition.json +0 -257
  210. package/dist/modules/workflows/examples/order-approval-guard-rules.json +0 -32
  211. package/dist/modules/workflows/lib/event-trigger-service.js +0 -308
  212. package/dist/modules/workflows/lib/event-trigger-service.js.map +0 -7
  213. package/dist/modules/workflows/migrations/Migration20260123143500.js +0 -36
  214. package/dist/modules/workflows/migrations/Migration20260123143500.js.map +0 -7
  215. package/dist/modules/workflows/subscribers/event-trigger.js +0 -78
  216. package/dist/modules/workflows/subscribers/event-trigger.js.map +0 -7
  217. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +0 -323
  218. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +0 -7
  219. package/dist/modules/workflows/widgets/injection/order-approval/widget.js +0 -17
  220. package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +0 -7
  221. package/dist/modules/workflows/widgets/injection-table.js +0 -19
  222. package/dist/modules/workflows/widgets/injection-table.js.map +0 -7
  223. package/generated/entities/workflow_event_trigger/index.ts +0 -15
  224. package/src/modules/auth/events.ts +0 -39
  225. package/src/modules/business_rules/api/execute/[ruleId]/route.ts +0 -163
  226. package/src/modules/catalog/events.ts +0 -45
  227. package/src/modules/customers/events.ts +0 -63
  228. package/src/modules/directory/events.ts +0 -31
  229. package/src/modules/sales/events.ts +0 -82
  230. package/src/modules/sales/lib/frontend/documentDataEvents.ts +0 -28
  231. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +0 -581
  232. package/src/modules/workflows/components/EventTriggersEditor.tsx +0 -664
  233. package/src/modules/workflows/events.ts +0 -49
  234. package/src/modules/workflows/examples/order-approval-definition.json +0 -257
  235. package/src/modules/workflows/examples/order-approval-guard-rules.json +0 -32
  236. package/src/modules/workflows/lib/event-trigger-service.ts +0 -557
  237. package/src/modules/workflows/migrations/Migration20260123143500.ts +0 -38
  238. package/src/modules/workflows/subscribers/event-trigger.ts +0 -109
  239. package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +0 -446
  240. package/src/modules/workflows/widgets/injection/order-approval/widget.ts +0 -16
  241. package/src/modules/workflows/widgets/injection-table.ts +0 -21
@@ -4,12 +4,8 @@ import { Role, RoleAcl, User, UserRole } from '@open-mercato/core/modules/auth/d
4
4
  import { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'
5
5
  import { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'
6
6
  import { normalizeTenantId } from './tenantAccess'
7
- import { SalesSettings, SalesDocumentSequence } from '@open-mercato/core/modules/sales/data/entities'
8
- import {
9
- DEFAULT_ORDER_NUMBER_FORMAT,
10
- DEFAULT_QUOTE_NUMBER_FORMAT,
11
- } from '@open-mercato/core/modules/sales/lib/documentNumberTokens'
12
7
  import { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'
8
+ import type { Module } from '@open-mercato/shared/modules/registry'
13
9
  import { isEncryptionDebugEnabled, isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'
14
10
  import { EncryptionMap } from '@open-mercato/core/modules/entities/data/entities'
15
11
  import { DEFAULT_ENCRYPTION_MAPS } from '@open-mercato/core/modules/entities/lib/encryptionDefaults'
@@ -95,6 +91,8 @@ export type SetupInitialTenantOptions = {
95
91
  failIfUserExists?: boolean
96
92
  primaryUserRoles?: string[]
97
93
  includeSuperadminRole?: boolean
94
+ /** Optional list of enabled modules. When provided, module setup hooks are called. */
95
+ modules?: Module[]
98
96
  }
99
97
 
100
98
  export type SetupInitialTenantResult = {
@@ -321,9 +319,16 @@ export async function setupInitialTenant(
321
319
  await rebuildHierarchyForTenant(em, tenantId)
322
320
  }
323
321
 
324
- await ensureDefaultRoleAcls(em, tenantId, { includeSuperadminRole })
322
+ const resolvedModules = options.modules ?? tryGetModules()
323
+ await ensureDefaultRoleAcls(em, tenantId, resolvedModules, { includeSuperadminRole })
325
324
  await deactivateDemoSuperAdminIfSelfOnboardingEnabled(em)
326
- await ensureSalesNumberingDefaults(em, { tenantId, organizationId })
325
+
326
+ // Call module onTenantCreated hooks
327
+ for (const mod of resolvedModules) {
328
+ if (mod.setup?.onTenantCreated) {
329
+ await mod.setup.onTenantCreated({ em, tenantId, organizationId })
330
+ }
331
+ }
327
332
 
328
333
  return {
329
334
  tenantId,
@@ -349,6 +354,7 @@ async function resolvePasswordHash(input: PrimaryUserInput): Promise<string | nu
349
354
  async function ensureDefaultRoleAcls(
350
355
  em: EntityManager,
351
356
  tenantId: string,
357
+ modules: Module[],
352
358
  options: { includeSuperadminRole?: boolean } = {},
353
359
  ) {
354
360
  const includeSuperadminRole = options.includeSuperadminRole ?? true
@@ -357,84 +363,27 @@ async function ensureDefaultRoleAcls(
357
363
  const adminRole = await findRoleByName(em, 'admin', roleTenantId)
358
364
  const employeeRole = await findRoleByName(em, 'employee', roleTenantId)
359
365
 
366
+ // Merge features from all enabled modules' setup configs
367
+ const superadminFeatures: string[] = []
368
+ const adminFeatures: string[] = []
369
+ const employeeFeatures: string[] = []
370
+
371
+ for (const mod of modules) {
372
+ const roleFeatures = mod.setup?.defaultRoleFeatures
373
+ if (!roleFeatures) continue
374
+ if (roleFeatures.superadmin) superadminFeatures.push(...roleFeatures.superadmin)
375
+ if (roleFeatures.admin) adminFeatures.push(...roleFeatures.admin)
376
+ if (roleFeatures.employee) employeeFeatures.push(...roleFeatures.employee)
377
+ }
378
+
360
379
  if (includeSuperadminRole && superadminRole) {
361
- await ensureRoleAclFor(em, superadminRole, tenantId, ['directory.tenants.*'], { isSuperAdmin: true })
380
+ await ensureRoleAclFor(em, superadminRole, tenantId, superadminFeatures, { isSuperAdmin: true })
362
381
  }
363
382
  if (adminRole) {
364
- const adminFeatures = [
365
- 'auth.*',
366
- 'entities.*',
367
- 'attachments.*',
368
- 'attachments.view',
369
- 'attachments.manage',
370
- 'query_index.*',
371
- 'search.*',
372
- 'vector.*',
373
- 'feature_toggles.*',
374
- 'configs.system_status.view',
375
- 'configs.cache.view',
376
- 'configs.cache.manage',
377
- 'configs.manage',
378
- 'catalog.*',
379
- 'catalog.variants.manage',
380
- 'catalog.pricing.manage',
381
- 'sales.*',
382
- 'audit_logs.*',
383
- 'directory.organizations.view',
384
- 'directory.organizations.manage',
385
- 'customers.*',
386
- 'customers.people.view',
387
- 'customers.people.manage',
388
- 'customers.companies.view',
389
- 'customers.companies.manage',
390
- 'customers.deals.view',
391
- 'customers.deals.manage',
392
- 'dictionaries.view',
393
- 'dictionaries.manage',
394
- 'example.*',
395
- 'dashboards.*',
396
- 'dashboards.admin.assign-widgets',
397
- 'analytics.view',
398
- 'api_keys.*',
399
- 'perspectives.use',
400
- 'perspectives.role_defaults',
401
- 'business_rules.*',
402
- 'workflows.*',
403
- 'currencies.*',
404
- 'staff.*',
405
- 'staff.leave_requests.manage',
406
- 'resources.*',
407
- 'planner.*',
408
- ]
409
- await ensureRoleAclFor(em, adminRole, tenantId, adminFeatures, { remove: ['directory.organizations.*', 'directory.tenants.*'] })
383
+ await ensureRoleAclFor(em, adminRole, tenantId, adminFeatures)
410
384
  }
411
385
  if (employeeRole) {
412
- await ensureRoleAclFor(em, employeeRole, tenantId, [
413
- 'customers.*',
414
- 'customers.people.view',
415
- 'customers.people.manage',
416
- 'customers.companies.view',
417
- 'customers.companies.manage',
418
- 'vector.*',
419
- 'catalog.*',
420
- 'catalog.variants.manage',
421
- 'catalog.pricing.manage',
422
- 'sales.*',
423
- 'dictionaries.view',
424
- 'example.*',
425
- 'example.widgets.*',
426
- 'dashboards.view',
427
- 'dashboards.configure',
428
- 'analytics.view',
429
- 'audit_logs.undo_self',
430
- 'perspectives.use',
431
- 'staff.leave_requests.send',
432
- 'staff.my_availability.view',
433
- 'staff.my_availability.manage',
434
- 'staff.my_leave_requests.view',
435
- 'staff.my_leave_requests.send',
436
- 'planner.view',
437
- ])
386
+ await ensureRoleAclFor(em, employeeRole, tenantId, employeeFeatures)
438
387
  }
439
388
  }
440
389
 
@@ -443,7 +392,7 @@ async function ensureRoleAclFor(
443
392
  role: Role,
444
393
  tenantId: string,
445
394
  features: string[],
446
- options: { isSuperAdmin?: boolean; remove?: string[] } = {},
395
+ options: { isSuperAdmin?: boolean } = {},
447
396
  ) {
448
397
  const existing = await em.findOne(RoleAcl, { role, tenantId })
449
398
  if (!existing) {
@@ -459,24 +408,10 @@ async function ensureRoleAclFor(
459
408
  }
460
409
  const currentFeatures = Array.isArray(existing.featuresJson) ? existing.featuresJson : []
461
410
  const merged = Array.from(new Set([...currentFeatures, ...features]))
462
- const removeSet = new Set(options.remove ?? [])
463
- const sanitized =
464
- removeSet.size
465
- ? merged.filter((value) => {
466
- if (removeSet.has(value)) return false
467
- for (const entry of removeSet) {
468
- if (entry.endsWith('.*')) {
469
- const prefix = entry.slice(0, -1) // keep trailing dot
470
- if (value === entry || value.startsWith(prefix)) return false
471
- }
472
- }
473
- return true
474
- })
475
- : merged
476
411
  const changed =
477
- sanitized.length !== currentFeatures.length ||
478
- sanitized.some((value, index) => value !== currentFeatures[index])
479
- if (changed) existing.featuresJson = sanitized
412
+ merged.length !== currentFeatures.length ||
413
+ merged.some((value, index) => value !== currentFeatures[index])
414
+ if (changed) existing.featuresJson = merged
480
415
  if (options.isSuperAdmin && !existing.isSuperAdmin) {
481
416
  existing.isSuperAdmin = true
482
417
  }
@@ -507,84 +442,12 @@ async function deactivateDemoSuperAdminIfSelfOnboardingEnabled(em: EntityManager
507
442
  }
508
443
  }
509
444
 
510
- async function ensureSalesNumberingDefaults(
511
- em: EntityManager,
512
- scope: { tenantId: string; organizationId: string },
513
- ) {
514
- const repo = (em as any).getRepository?.(SalesSettings)
515
- const findSettings = async () =>
516
- repo?.findOne({
517
- tenantId: scope.tenantId,
518
- organizationId: scope.organizationId,
519
- }) ??
520
- (em as any).findOne?.(SalesSettings, {
521
- tenantId: scope.tenantId,
522
- organizationId: scope.organizationId,
523
- })
524
-
525
- const exists = await findSettings()
526
- if (!exists) {
527
- const settings =
528
- repo?.create?.({
529
- tenantId: scope.tenantId,
530
- organizationId: scope.organizationId,
531
- orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,
532
- quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,
533
- createdAt: new Date(),
534
- updatedAt: new Date(),
535
- }) ??
536
- (em as any).create?.(SalesSettings, {
537
- tenantId: scope.tenantId,
538
- organizationId: scope.organizationId,
539
- orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,
540
- quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,
541
- createdAt: new Date(),
542
- updatedAt: new Date(),
543
- })
544
- if (settings && (em as any).persist) {
545
- em.persist(settings)
546
- }
547
- }
548
-
549
- const sequenceRepo = (em as any).getRepository?.(SalesDocumentSequence)
550
- const kinds: Array<'order' | 'quote'> = ['order', 'quote']
551
- for (const kind of kinds) {
552
- const seq =
553
- sequenceRepo?.findOne({
554
- tenantId: scope.tenantId,
555
- organizationId: scope.organizationId,
556
- documentKind: kind,
557
- }) ??
558
- (em as any).findOne?.(SalesDocumentSequence, {
559
- tenantId: scope.tenantId,
560
- organizationId: scope.organizationId,
561
- documentKind: kind,
562
- })
563
- if (!seq) {
564
- const entry =
565
- sequenceRepo?.create?.({
566
- tenantId: scope.tenantId,
567
- organizationId: scope.organizationId,
568
- documentKind: kind,
569
- currentValue: 0,
570
- createdAt: new Date(),
571
- updatedAt: new Date(),
572
- }) ??
573
- (em as any).create?.(SalesDocumentSequence, {
574
- tenantId: scope.tenantId,
575
- organizationId: scope.organizationId,
576
- documentKind: kind,
577
- currentValue: 0,
578
- createdAt: new Date(),
579
- updatedAt: new Date(),
580
- })
581
- if (entry && (em as any).persist) {
582
- em.persist(entry)
583
- }
584
- }
585
- }
586
-
587
- if ((em as any).flush) {
588
- await em.flush()
445
+ /** Try to get modules from runtime registry; returns empty array if not yet registered. */
446
+ function tryGetModules(): Module[] {
447
+ try {
448
+ const { getModules } = require('@open-mercato/shared/lib/modules/registry')
449
+ return getModules()
450
+ } catch {
451
+ return []
589
452
  }
590
453
  }
@@ -0,0 +1,9 @@
1
+ import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+
3
+ export const setup: ModuleSetupConfig = {
4
+ defaultRoleFeatures: {
5
+ admin: ['auth.*'],
6
+ },
7
+ }
8
+
9
+ export default setup
@@ -287,43 +287,3 @@ export const ruleDiscoveryOptionsSchema = z.object({
287
287
  })
288
288
 
289
289
  export type RuleDiscoveryOptionsInput = z.infer<typeof ruleDiscoveryOptionsSchema>
290
-
291
- // Direct Rule Execution Context Schema (for executing a specific rule by ID)
292
- export const directRuleExecutionContextSchema = z.object({
293
- ruleId: z.uuid('ruleId must be a valid UUID'),
294
- data: z.any(),
295
- user: z.looseObject({
296
- id: z.string().optional(),
297
- email: z.string().optional(),
298
- role: z.string().optional(),
299
- }).optional(),
300
- tenantId: z.uuid('tenantId must be a valid UUID'),
301
- organizationId: z.uuid('organizationId must be a valid UUID'),
302
- executedBy: z.string().optional(),
303
- dryRun: z.boolean().optional(),
304
- entityType: z.string().optional(),
305
- entityId: z.string().optional(),
306
- eventType: z.string().optional(),
307
- })
308
-
309
- export type DirectRuleExecutionContextInput = z.infer<typeof directRuleExecutionContextSchema>
310
-
311
- // Rule ID Execution Context Schema (for executing a specific rule by its string rule_id identifier)
312
- export const ruleIdExecutionContextSchema = z.object({
313
- ruleId: z.string().min(1, 'ruleId must be a non-empty string').max(50),
314
- data: z.any(),
315
- user: z.looseObject({
316
- id: z.string().optional(),
317
- email: z.string().optional(),
318
- role: z.string().optional(),
319
- }).optional(),
320
- tenantId: z.uuid('tenantId must be a valid UUID'),
321
- organizationId: z.uuid('organizationId must be a valid UUID'),
322
- executedBy: z.string().optional(),
323
- dryRun: z.boolean().optional(),
324
- entityType: z.string().optional(),
325
- entityId: z.string().optional(),
326
- eventType: z.string().optional(),
327
- })
328
-
329
- export type RuleIdExecutionContextInput = z.infer<typeof ruleIdExecutionContextSchema>
@@ -8,28 +8,3 @@ export const metadata: ModuleInfo = {
8
8
  author: 'Patryk Lewczuk',
9
9
  license: 'Proprietary',
10
10
  }
11
-
12
- // Export rule engine types and functions for programmatic usage
13
- export {
14
- executeRules,
15
- executeRuleById,
16
- executeRuleByRuleId,
17
- executeSingleRule,
18
- findApplicableRules,
19
- logRuleExecution,
20
- type RuleEngineContext,
21
- type RuleEngineResult,
22
- type RuleExecutionResult,
23
- type RuleDiscoveryOptions,
24
- type DirectRuleExecutionContext,
25
- type DirectRuleExecutionResult,
26
- type RuleIdExecutionContext,
27
- } from './lib/rule-engine'
28
-
29
- // Export validator schemas
30
- export {
31
- directRuleExecutionContextSchema,
32
- ruleIdExecutionContextSchema,
33
- type DirectRuleExecutionContextInput,
34
- type RuleIdExecutionContextInput,
35
- } from './data/validators'
@@ -4,7 +4,7 @@ import * as ruleEvaluator from './rule-evaluator'
4
4
  import * as actionExecutor from './action-executor'
5
5
  import type { RuleEvaluationContext } from './rule-evaluator'
6
6
  import type { ActionContext, ActionExecutionOutcome } from './action-executor'
7
- import { ruleEngineContextSchema, ruleDiscoveryOptionsSchema, directRuleExecutionContextSchema, ruleIdExecutionContextSchema } from '../data/validators'
7
+ import { ruleEngineContextSchema, ruleDiscoveryOptionsSchema } from '../data/validators'
8
8
 
9
9
  /**
10
10
  * Constants
@@ -85,65 +85,6 @@ export interface RuleDiscoveryOptions {
85
85
  ruleType?: RuleType
86
86
  }
87
87
 
88
- /**
89
- * Direct rule execution context (for executing a specific rule by ID)
90
- */
91
- export interface DirectRuleExecutionContext {
92
- ruleId: string // Database UUID of the rule
93
- data: any
94
- user?: {
95
- id?: string
96
- email?: string
97
- role?: string
98
- [key: string]: any
99
- }
100
- tenantId: string
101
- organizationId: string
102
- executedBy?: string
103
- dryRun?: boolean
104
- // Optional for logging (falls back to rule's entityType)
105
- entityType?: string
106
- entityId?: string
107
- eventType?: string
108
- }
109
-
110
- /**
111
- * Direct rule execution result
112
- */
113
- export interface DirectRuleExecutionResult {
114
- success: boolean
115
- ruleId: string
116
- ruleName: string
117
- conditionResult: boolean
118
- actionsExecuted: ActionExecutionOutcome | null
119
- executionTime: number
120
- error?: string
121
- logId?: string
122
- }
123
-
124
- /**
125
- * Context for executing a rule by its string rule_id identifier
126
- * Unlike DirectRuleExecutionContext which uses database UUID,
127
- * this uses the string identifier (e.g., "workflow_checkout_inventory_available")
128
- */
129
- export interface RuleIdExecutionContext {
130
- ruleId: string // String identifier (e.g., "workflow_checkout_inventory_available")
131
- data: any
132
- user?: {
133
- id?: string
134
- email?: string
135
- role?: string
136
- [key: string]: any
137
- }
138
- tenantId: string
139
- organizationId: string
140
- executedBy?: string
141
- dryRun?: boolean
142
- entityType?: string
143
- entityId?: string
144
- eventType?: string
145
- }
146
-
147
88
  /**
148
89
  * Execute a function with a timeout
149
90
  */
@@ -463,227 +404,6 @@ export async function findApplicableRules(
463
404
  })
464
405
  }
465
406
 
466
- /**
467
- * Execute a specific rule by its database UUID
468
- * This bypasses the entityType/eventType discovery mechanism and directly executes the rule
469
- */
470
- export async function executeRuleById(
471
- em: EntityManager,
472
- context: DirectRuleExecutionContext
473
- ): Promise<DirectRuleExecutionResult> {
474
- const startTime = Date.now()
475
-
476
- // Validate input
477
- const validation = directRuleExecutionContextSchema.safeParse(context)
478
- if (!validation.success) {
479
- const validationErrors = validation.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)
480
- return {
481
- success: false,
482
- ruleId: context.ruleId,
483
- ruleName: 'Unknown',
484
- conditionResult: false,
485
- actionsExecuted: null,
486
- executionTime: Date.now() - startTime,
487
- error: `Validation failed: ${validationErrors.join(', ')}`,
488
- }
489
- }
490
-
491
- // Fetch rule by ID with tenant/org validation
492
- const rule = await em.findOne(BusinessRule, {
493
- id: context.ruleId,
494
- tenantId: context.tenantId,
495
- organizationId: context.organizationId,
496
- deletedAt: null,
497
- })
498
-
499
- if (!rule) {
500
- return {
501
- success: false,
502
- ruleId: context.ruleId,
503
- ruleName: 'Unknown',
504
- conditionResult: false,
505
- actionsExecuted: null,
506
- executionTime: Date.now() - startTime,
507
- error: 'Rule not found',
508
- }
509
- }
510
-
511
- if (!rule.enabled) {
512
- return {
513
- success: false,
514
- ruleId: rule.ruleId,
515
- ruleName: rule.ruleName,
516
- conditionResult: false,
517
- actionsExecuted: null,
518
- executionTime: Date.now() - startTime,
519
- error: 'Rule is disabled',
520
- }
521
- }
522
-
523
- // Check effective date range
524
- const now = new Date()
525
- if (rule.effectiveFrom && rule.effectiveFrom > now) {
526
- return {
527
- success: false,
528
- ruleId: rule.ruleId,
529
- ruleName: rule.ruleName,
530
- conditionResult: false,
531
- actionsExecuted: null,
532
- executionTime: Date.now() - startTime,
533
- error: `Rule is not yet effective (starts ${rule.effectiveFrom.toISOString()})`,
534
- }
535
- }
536
- if (rule.effectiveTo && rule.effectiveTo < now) {
537
- return {
538
- success: false,
539
- ruleId: rule.ruleId,
540
- ruleName: rule.ruleName,
541
- conditionResult: false,
542
- actionsExecuted: null,
543
- executionTime: Date.now() - startTime,
544
- error: `Rule has expired (ended ${rule.effectiveTo.toISOString()})`,
545
- }
546
- }
547
-
548
- // Build RuleEngineContext (use provided entityType or fall back to rule's)
549
- const engineContext: RuleEngineContext = {
550
- entityType: context.entityType || rule.entityType,
551
- entityId: context.entityId,
552
- eventType: context.eventType || rule.eventType || undefined,
553
- data: context.data,
554
- user: context.user,
555
- tenantId: context.tenantId,
556
- organizationId: context.organizationId,
557
- executedBy: context.executedBy,
558
- dryRun: context.dryRun,
559
- }
560
-
561
- // Execute via existing executeSingleRule
562
- const result = await executeSingleRule(em, rule, engineContext)
563
-
564
- return {
565
- success: !result.error,
566
- ruleId: rule.ruleId,
567
- ruleName: rule.ruleName,
568
- conditionResult: result.conditionResult,
569
- actionsExecuted: result.actionsExecuted,
570
- executionTime: result.executionTime,
571
- error: result.error,
572
- logId: result.logId,
573
- }
574
- }
575
-
576
- /**
577
- * Execute a rule by its string rule_id identifier
578
- * Looks up rule by rule_id (string column) + tenant_id (unique constraint)
579
- * This is useful for workflow conditions that reference rules by their string identifiers
580
- */
581
- export async function executeRuleByRuleId(
582
- em: EntityManager,
583
- context: RuleIdExecutionContext
584
- ): Promise<DirectRuleExecutionResult> {
585
- const startTime = Date.now()
586
-
587
- // Validate input
588
- const validation = ruleIdExecutionContextSchema.safeParse(context)
589
- if (!validation.success) {
590
- const validationErrors = validation.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)
591
- return {
592
- success: false,
593
- ruleId: context.ruleId || 'unknown',
594
- ruleName: 'Unknown',
595
- conditionResult: false,
596
- actionsExecuted: null,
597
- executionTime: Date.now() - startTime,
598
- error: `Validation failed: ${validationErrors.join(', ')}`,
599
- }
600
- }
601
-
602
- // Fetch rule by rule_id (string identifier) + tenant/org
603
- const rule = await em.findOne(BusinessRule, {
604
- ruleId: context.ruleId, // String identifier column
605
- tenantId: context.tenantId,
606
- organizationId: context.organizationId,
607
- deletedAt: null,
608
- })
609
-
610
- if (!rule) {
611
- return {
612
- success: false,
613
- ruleId: context.ruleId,
614
- ruleName: 'Unknown',
615
- conditionResult: false,
616
- actionsExecuted: null,
617
- executionTime: Date.now() - startTime,
618
- error: 'Rule not found',
619
- }
620
- }
621
-
622
- if (!rule.enabled) {
623
- return {
624
- success: false,
625
- ruleId: rule.ruleId,
626
- ruleName: rule.ruleName,
627
- conditionResult: false,
628
- actionsExecuted: null,
629
- executionTime: Date.now() - startTime,
630
- error: 'Rule is disabled',
631
- }
632
- }
633
-
634
- // Check effective date range
635
- const now = new Date()
636
- if (rule.effectiveFrom && rule.effectiveFrom > now) {
637
- return {
638
- success: false,
639
- ruleId: rule.ruleId,
640
- ruleName: rule.ruleName,
641
- conditionResult: false,
642
- actionsExecuted: null,
643
- executionTime: Date.now() - startTime,
644
- error: `Rule is not yet effective (starts ${rule.effectiveFrom.toISOString()})`,
645
- }
646
- }
647
- if (rule.effectiveTo && rule.effectiveTo < now) {
648
- return {
649
- success: false,
650
- ruleId: rule.ruleId,
651
- ruleName: rule.ruleName,
652
- conditionResult: false,
653
- actionsExecuted: null,
654
- executionTime: Date.now() - startTime,
655
- error: `Rule has expired (ended ${rule.effectiveTo.toISOString()})`,
656
- }
657
- }
658
-
659
- // Build RuleEngineContext (use provided entityType or fall back to rule's)
660
- const engineContext: RuleEngineContext = {
661
- entityType: context.entityType || rule.entityType,
662
- entityId: context.entityId,
663
- eventType: context.eventType || rule.eventType || undefined,
664
- data: context.data,
665
- user: context.user,
666
- tenantId: context.tenantId,
667
- organizationId: context.organizationId,
668
- executedBy: context.executedBy,
669
- dryRun: context.dryRun,
670
- }
671
-
672
- // Execute via existing executeSingleRule
673
- const result = await executeSingleRule(em, rule, engineContext)
674
-
675
- return {
676
- success: !result.error,
677
- ruleId: rule.ruleId,
678
- ruleName: rule.ruleName,
679
- conditionResult: result.conditionResult,
680
- actionsExecuted: result.actionsExecuted,
681
- executionTime: result.executionTime,
682
- error: result.error,
683
- logId: result.logId,
684
- }
685
- }
686
-
687
407
  /**
688
408
  * Sensitive field patterns to exclude from logs
689
409
  */
@@ -0,0 +1,9 @@
1
+ import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+
3
+ export const setup: ModuleSetupConfig = {
4
+ defaultRoleFeatures: {
5
+ admin: ['business_rules.*'],
6
+ },
7
+ }
8
+
9
+ export default setup
@@ -0,0 +1,22 @@
1
+ import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+ import { seedCatalogUnits, seedCatalogPriceKinds, seedCatalogExamplesForScope } from './lib/seeds'
3
+
4
+ export const setup: ModuleSetupConfig = {
5
+ seedDefaults: async (ctx) => {
6
+ const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
7
+ await seedCatalogUnits(ctx.em, scope)
8
+ await seedCatalogPriceKinds(ctx.em, scope)
9
+ },
10
+
11
+ seedExamples: async (ctx) => {
12
+ const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
13
+ await seedCatalogExamplesForScope(ctx.em, ctx.container, scope)
14
+ },
15
+
16
+ defaultRoleFeatures: {
17
+ admin: ['catalog.*', 'catalog.variants.manage', 'catalog.pricing.manage'],
18
+ employee: ['catalog.*', 'catalog.variants.manage', 'catalog.pricing.manage'],
19
+ },
20
+ }
21
+
22
+ export default setup