@open-mercato/core 0.6.4-develop.4239.1.4a264a5828 → 0.6.4-develop.4254.1.7a123d970c

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 (80) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/authFixtures.js +70 -1
  3. package/dist/helpers/integration/authFixtures.js.map +2 -2
  4. package/dist/helpers/integration/dbFixtures.js +98 -0
  5. package/dist/helpers/integration/dbFixtures.js.map +7 -0
  6. package/dist/modules/business_rules/api/execute/route.js +2 -1
  7. package/dist/modules/business_rules/api/execute/route.js.map +2 -2
  8. package/dist/modules/business_rules/api/rules/route.js +10 -0
  9. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  10. package/dist/modules/business_rules/cli.js +6 -0
  11. package/dist/modules/business_rules/cli.js.map +2 -2
  12. package/dist/modules/business_rules/lib/rule-engine.js +116 -9
  13. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  14. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js +3 -2
  15. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js.map +2 -2
  16. package/dist/modules/catalog/api/products/route.js +21 -4
  17. package/dist/modules/catalog/api/products/route.js.map +2 -2
  18. package/dist/modules/catalog/lib/pricing.js +6 -0
  19. package/dist/modules/catalog/lib/pricing.js.map +2 -2
  20. package/dist/modules/catalog/services/catalogPricingService.js +5 -1
  21. package/dist/modules/catalog/services/catalogPricingService.js.map +2 -2
  22. package/dist/modules/customers/api/activities/route.js +15 -2
  23. package/dist/modules/customers/api/activities/route.js.map +2 -2
  24. package/dist/modules/customers/api/comments/route.js +15 -3
  25. package/dist/modules/customers/api/comments/route.js.map +2 -2
  26. package/dist/modules/customers/api/companies/[id]/people/route.js +2 -4
  27. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  28. package/dist/modules/customers/api/companies/[id]/route.js +2 -4
  29. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  30. package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -4
  31. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  32. package/dist/modules/customers/api/deals/[id]/people/route.js +2 -4
  33. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  34. package/dist/modules/customers/api/deals/[id]/route.js +2 -9
  35. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  36. package/dist/modules/customers/api/deals/[id]/stats/route.js +2 -9
  37. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
  38. package/dist/modules/customers/api/entity-roles-factory.js +2 -8
  39. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  40. package/dist/modules/customers/api/people/[id]/companies/context.js +2 -4
  41. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  42. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -4
  43. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  44. package/dist/modules/customers/api/people/[id]/route.js +2 -4
  45. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  46. package/dist/modules/directory/utils/organizationScopeGuard.js +22 -0
  47. package/dist/modules/directory/utils/organizationScopeGuard.js.map +7 -0
  48. package/dist/modules/workflows/cli.js +8 -0
  49. package/dist/modules/workflows/cli.js.map +2 -2
  50. package/dist/modules/workflows/lib/seeds.js +8 -4
  51. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  52. package/dist/modules/workflows/setup.js +3 -1
  53. package/dist/modules/workflows/setup.js.map +2 -2
  54. package/package.json +7 -7
  55. package/src/helpers/integration/authFixtures.ts +98 -0
  56. package/src/helpers/integration/dbFixtures.ts +144 -0
  57. package/src/modules/business_rules/api/execute/route.ts +2 -1
  58. package/src/modules/business_rules/api/rules/route.ts +10 -0
  59. package/src/modules/business_rules/cli.ts +6 -0
  60. package/src/modules/business_rules/lib/rule-engine.ts +163 -9
  61. package/src/modules/business_rules/subscribers/crud-rule-trigger.ts +3 -2
  62. package/src/modules/catalog/api/products/route.ts +23 -4
  63. package/src/modules/catalog/lib/pricing.ts +9 -0
  64. package/src/modules/catalog/services/catalogPricingService.ts +6 -0
  65. package/src/modules/customers/api/activities/route.ts +16 -5
  66. package/src/modules/customers/api/comments/route.ts +15 -5
  67. package/src/modules/customers/api/companies/[id]/people/route.ts +2 -4
  68. package/src/modules/customers/api/companies/[id]/route.ts +2 -5
  69. package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -4
  70. package/src/modules/customers/api/deals/[id]/people/route.ts +2 -4
  71. package/src/modules/customers/api/deals/[id]/route.ts +2 -9
  72. package/src/modules/customers/api/deals/[id]/stats/route.ts +2 -9
  73. package/src/modules/customers/api/entity-roles-factory.ts +2 -12
  74. package/src/modules/customers/api/people/[id]/companies/context.ts +2 -5
  75. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -5
  76. package/src/modules/customers/api/people/[id]/route.ts +2 -5
  77. package/src/modules/directory/utils/organizationScopeGuard.ts +39 -0
  78. package/src/modules/workflows/cli.ts +8 -0
  79. package/src/modules/workflows/lib/seeds.ts +13 -3
  80. package/src/modules/workflows/setup.ts +3 -1
@@ -0,0 +1,39 @@
1
+ import type { AuthContext } from '@open-mercato/shared/lib/auth/server'
2
+ import { isOrganizationAccessAllowed } from '@open-mercato/shared/lib/auth/organizationAccess'
3
+ import type { OrganizationScope } from './organizationScope'
4
+
5
+ export type OrganizationReadAccessInput = {
6
+ scope: OrganizationScope | null | undefined
7
+ auth: AuthContext
8
+ organizationId: string | null
9
+ }
10
+
11
+ /**
12
+ * Fail-closed read guard for single-record detail routes. Centralizes the
13
+ * decision so callers keep their own deny mechanism (throw / return response)
14
+ * and their own i18n key.
15
+ *
16
+ * Unrestricted access (super admin or `scope.allowedIds === null`) is the only
17
+ * bypass. For a restricted principal the allowed set is derived the same way
18
+ * the detail routes always have (`filterIds` narrows the active view, else the
19
+ * principal's home org); an empty derived set denies instead of skipping.
20
+ */
21
+ export function isOrganizationReadAccessAllowed(input: OrganizationReadAccessInput): boolean {
22
+ const isSuperAdmin = input.auth?.isSuperAdmin === true
23
+ if (isSuperAdmin || input.scope?.allowedIds === null) return true
24
+
25
+ const allowedOrganizationIds = new Set<string>()
26
+ if (input.scope?.filterIds?.length) {
27
+ for (const id of input.scope.filterIds) {
28
+ if (typeof id === 'string' && id.trim().length) allowedOrganizationIds.add(id)
29
+ }
30
+ } else if (input.auth?.orgId) {
31
+ allowedOrganizationIds.add(input.auth.orgId)
32
+ }
33
+
34
+ return isOrganizationAccessAllowed({
35
+ isSuperAdmin,
36
+ allowedOrganizationIds: Array.from(allowedOrganizationIds),
37
+ targetOrganizationId: input.organizationId,
38
+ })
39
+ }
@@ -4,6 +4,10 @@ import { getRedisUrl, getRedisUrlOrThrow } from '@open-mercato/shared/lib/redis/
4
4
  import type { EntityManager } from '@mikro-orm/postgresql'
5
5
  import { WorkflowDefinition } from './data/entities'
6
6
  import { BusinessRule, type RuleType } from '@open-mercato/core/modules/business_rules/data/entities'
7
+ import {
8
+ invalidateBusinessRuleDiscoveryCache,
9
+ resolveBusinessRuleDiscoveryCache,
10
+ } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
7
11
  import * as fs from 'fs'
8
12
  import * as path from 'path'
9
13
  import { fileURLToPath } from 'url'
@@ -69,6 +73,7 @@ const seedDemoWithRules: ModuleCli = {
69
73
  try {
70
74
  const { resolve } = await createRequestContainer()
71
75
  const em = resolve<EntityManager>('em')
76
+ const cache = resolveBusinessRuleDiscoveryCache(resolve)
72
77
 
73
78
  // Import BusinessRule entity
74
79
  const { BusinessRule } = await import('../business_rules/data/entities')
@@ -100,6 +105,7 @@ const seedDemoWithRules: ModuleCli = {
100
105
  })
101
106
 
102
107
  await em.persist(rule).flush()
108
+ await invalidateBusinessRuleDiscoveryCache(cache, tenantId, organizationId)
103
109
  console.log(` ✓ Seeded guard rule: ${rule.ruleName}`)
104
110
  seededCount++
105
111
  }
@@ -211,6 +217,7 @@ const seedOrderApproval: ModuleCli = {
211
217
  try {
212
218
  const { resolve } = await createRequestContainer()
213
219
  const em = resolve<EntityManager>('em')
220
+ const cache = resolveBusinessRuleDiscoveryCache(resolve)
214
221
 
215
222
  // 1. Seed order approval guard rules first
216
223
  const guardRulesPath = path.join(__dirname, 'examples', 'order-approval-guard-rules.json')
@@ -252,6 +259,7 @@ const seedOrderApproval: ModuleCli = {
252
259
 
253
260
  if (rulesSeeded > 0) {
254
261
  await em.flush()
262
+ await invalidateBusinessRuleDiscoveryCache(cache, tenantId, organizationId)
255
263
  }
256
264
 
257
265
  console.log(`✅ Seeded order approval guard rules`)
@@ -4,6 +4,10 @@ import * as path from 'path'
4
4
  import { fileURLToPath } from 'node:url'
5
5
  import { WorkflowDefinition, type WorkflowDefinitionData } from '../data/entities'
6
6
  import { BusinessRule, type RuleType } from '@open-mercato/core/modules/business_rules/data/entities'
7
+ import {
8
+ invalidateBusinessRuleDiscoveryCache,
9
+ type RuleDiscoveryCache,
10
+ } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
7
11
 
8
12
  const __esmDirname = path.dirname(fileURLToPath(import.meta.url))
9
13
 
@@ -130,6 +134,7 @@ async function seedGuardRules(
130
134
  em: EntityManager,
131
135
  scope: WorkflowSeedScope,
132
136
  fileName: string,
137
+ cache?: RuleDiscoveryCache | null,
133
138
  ): Promise<{ seeded: number; skipped: number; updated: number }> {
134
139
  const seeds = readExampleJson<GuardRuleSeed[]>(fileName)
135
140
  if (!Array.isArray(seeds)) {
@@ -185,16 +190,21 @@ async function seedGuardRules(
185
190
  }
186
191
  if (seeded > 0 || updated > 0) {
187
192
  await em.flush()
193
+ await invalidateBusinessRuleDiscoveryCache(cache, scope.tenantId, scope.organizationId)
188
194
  }
189
195
  return { seeded, skipped, updated }
190
196
  }
191
197
 
192
- export async function seedExampleWorkflows(em: EntityManager, scope: WorkflowSeedScope): Promise<void> {
198
+ export async function seedExampleWorkflows(
199
+ em: EntityManager,
200
+ scope: WorkflowSeedScope,
201
+ options: { cache?: RuleDiscoveryCache | null } = {},
202
+ ): Promise<void> {
193
203
  // workflows.checkout-demo and workflows.simple-approval are now code-defined
194
204
  // (see packages/core/src/modules/workflows/workflows.ts). Seeding DB rows for
195
205
  // them would shadow the code definitions in the merge layer, so they are no
196
206
  // longer seeded here. Existing tenants are migrated via Migration20260428102318.
197
- await seedGuardRules(em, scope, 'guard-rules-example.json')
207
+ await seedGuardRules(em, scope, 'guard-rules-example.json', options.cache)
198
208
  await seedWorkflowDefinition(em, scope, 'sales-pipeline-definition.json')
199
- await seedGuardRules(em, scope, 'order-approval-guard-rules.json')
209
+ await seedGuardRules(em, scope, 'order-approval-guard-rules.json', options.cache)
200
210
  }
@@ -1,10 +1,12 @@
1
1
  import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+ import { resolveBusinessRuleDiscoveryCache } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
2
3
  import { seedExampleWorkflows } from './lib/seeds'
3
4
 
4
5
  export const setup: ModuleSetupConfig = {
5
6
  seedDefaults: async (ctx) => {
6
7
  const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
7
- await seedExampleWorkflows(ctx.em, scope)
8
+ const cache = resolveBusinessRuleDiscoveryCache(ctx.container.resolve.bind(ctx.container))
9
+ await seedExampleWorkflows(ctx.em, scope, { cache })
8
10
  },
9
11
 
10
12
  defaultRoleFeatures: {