@open-mercato/core 0.6.4-develop.4239.1.4a264a5828 → 0.6.4-develop.4264.1.53368d85fe

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 (87) 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/customer_accounts/api/portal/events/stream.js +1 -0
  23. package/dist/modules/customer_accounts/api/portal/events/stream.js.map +2 -2
  24. package/dist/modules/customers/api/activities/route.js +15 -2
  25. package/dist/modules/customers/api/activities/route.js.map +2 -2
  26. package/dist/modules/customers/api/comments/route.js +15 -3
  27. package/dist/modules/customers/api/comments/route.js.map +2 -2
  28. package/dist/modules/customers/api/companies/[id]/people/route.js +2 -4
  29. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  30. package/dist/modules/customers/api/companies/[id]/route.js +2 -4
  31. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  32. package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -4
  33. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  34. package/dist/modules/customers/api/deals/[id]/people/route.js +2 -4
  35. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  36. package/dist/modules/customers/api/deals/[id]/route.js +2 -9
  37. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  38. package/dist/modules/customers/api/deals/[id]/stats/route.js +2 -9
  39. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
  40. package/dist/modules/customers/api/entity-roles-factory.js +2 -8
  41. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  42. package/dist/modules/customers/api/people/[id]/companies/context.js +2 -4
  43. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  44. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -4
  45. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  46. package/dist/modules/customers/api/people/[id]/route.js +2 -4
  47. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  48. package/dist/modules/directory/utils/organizationScopeGuard.js +22 -0
  49. package/dist/modules/directory/utils/organizationScopeGuard.js.map +7 -0
  50. package/dist/modules/workflows/cli.js +8 -0
  51. package/dist/modules/workflows/cli.js.map +2 -2
  52. package/dist/modules/workflows/lib/seeds.js +8 -4
  53. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  54. package/dist/modules/workflows/setup.js +3 -1
  55. package/dist/modules/workflows/setup.js.map +2 -2
  56. package/package.json +7 -7
  57. package/src/helpers/integration/authFixtures.ts +98 -0
  58. package/src/helpers/integration/dbFixtures.ts +144 -0
  59. package/src/modules/business_rules/api/execute/route.ts +2 -1
  60. package/src/modules/business_rules/api/rules/route.ts +10 -0
  61. package/src/modules/business_rules/cli.ts +6 -0
  62. package/src/modules/business_rules/lib/rule-engine.ts +163 -9
  63. package/src/modules/business_rules/subscribers/crud-rule-trigger.ts +3 -2
  64. package/src/modules/catalog/api/products/route.ts +23 -4
  65. package/src/modules/catalog/lib/pricing.ts +9 -0
  66. package/src/modules/catalog/services/catalogPricingService.ts +6 -0
  67. package/src/modules/customer_accounts/api/portal/events/stream.ts +6 -0
  68. package/src/modules/customers/api/activities/route.ts +16 -5
  69. package/src/modules/customers/api/comments/route.ts +15 -5
  70. package/src/modules/customers/api/companies/[id]/people/route.ts +2 -4
  71. package/src/modules/customers/api/companies/[id]/route.ts +2 -5
  72. package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -4
  73. package/src/modules/customers/api/deals/[id]/people/route.ts +2 -4
  74. package/src/modules/customers/api/deals/[id]/route.ts +2 -9
  75. package/src/modules/customers/api/deals/[id]/stats/route.ts +2 -9
  76. package/src/modules/customers/api/entity-roles-factory.ts +2 -12
  77. package/src/modules/customers/api/people/[id]/companies/context.ts +2 -5
  78. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -5
  79. package/src/modules/customers/api/people/[id]/route.ts +2 -5
  80. package/src/modules/directory/utils/organizationScopeGuard.ts +39 -0
  81. package/src/modules/staff/i18n/de.json +23 -0
  82. package/src/modules/staff/i18n/en.json +23 -0
  83. package/src/modules/staff/i18n/es.json +23 -0
  84. package/src/modules/staff/i18n/pl.json +23 -0
  85. package/src/modules/workflows/cli.ts +8 -0
  86. package/src/modules/workflows/lib/seeds.ts +13 -3
  87. package/src/modules/workflows/setup.ts +3 -1
@@ -1,4 +1,4 @@
1
- [build:core] found 2738 entry points
1
+ [build:core] found 2742 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 176 entry points
4
4
  [build:core:generated] built successfully
@@ -1,6 +1,18 @@
1
1
  import { expect } from "@playwright/test";
2
2
  import { apiRequest } from "./api.js";
3
3
  import { expectId, readJsonSafe } from "./generalFixtures.js";
4
+ const BASE_URL = process.env.BASE_URL?.trim() || null;
5
+ function resolveUrl(path) {
6
+ return BASE_URL ? `${BASE_URL}${path}` : path;
7
+ }
8
+ async function apiRequestWithSelectedOrg(request, method, path, options) {
9
+ const headers = {
10
+ Authorization: `Bearer ${options.token}`,
11
+ "Content-Type": "application/json",
12
+ Cookie: `om_selected_org=${options.selectedOrgId}`
13
+ };
14
+ return request.fetch(resolveUrl(path), { method, headers, data: options.data });
15
+ }
4
16
  async function createRoleFixture(request, token, input) {
5
17
  const payload = {
6
18
  name: input.name
@@ -33,10 +45,67 @@ async function deleteUserIfExists(request, token, userId) {
33
45
  if (!token || !userId) return;
34
46
  await apiRequest(request, "DELETE", `/api/auth/users?id=${encodeURIComponent(userId)}`, { token }).catch(() => void 0);
35
47
  }
48
+ async function createOrganizationFixture(request, token, input) {
49
+ const payload = { name: input.name };
50
+ if (typeof input.tenantId === "string" && input.tenantId.length > 0) {
51
+ payload.tenantId = input.tenantId;
52
+ }
53
+ const response = await apiRequest(request, "POST", "/api/directory/organizations", {
54
+ token,
55
+ data: payload
56
+ });
57
+ const body = await readJsonSafe(response);
58
+ expect(response.status(), "POST /api/directory/organizations should return 201").toBe(201);
59
+ return expectId(body?.id, "Organization creation response should include id");
60
+ }
61
+ async function deleteOrganizationIfExists(request, token, organizationId) {
62
+ if (!token || !organizationId) return;
63
+ await apiRequest(request, "DELETE", "/api/directory/organizations", {
64
+ token,
65
+ data: { id: organizationId }
66
+ }).catch(() => void 0);
67
+ }
68
+ async function setRoleAclFeatures(request, token, input) {
69
+ const payload = {
70
+ roleId: input.roleId,
71
+ features: input.features
72
+ };
73
+ if (input.organizations !== void 0) {
74
+ payload.organizations = input.organizations;
75
+ }
76
+ const response = await apiRequest(request, "PUT", "/api/auth/roles/acl", {
77
+ token,
78
+ data: payload
79
+ });
80
+ const body = await readJsonSafe(response);
81
+ expect(response.status(), "PUT /api/auth/roles/acl should return 200").toBe(200);
82
+ expect(body?.ok, "Role ACL update should report ok=true").toBe(true);
83
+ }
84
+ async function setUserAclVisibility(request, token, input) {
85
+ const payload = {
86
+ userId: input.userId,
87
+ organizations: input.organizations
88
+ };
89
+ if (input.features !== void 0) {
90
+ payload.features = input.features;
91
+ }
92
+ const response = await apiRequest(request, "PUT", "/api/auth/users/acl", {
93
+ token,
94
+ data: payload
95
+ });
96
+ const body = await readJsonSafe(response);
97
+ expect(response.status(), "PUT /api/auth/users/acl should return 200").toBe(200);
98
+ expect(body?.ok, "User ACL update should report ok=true").toBe(true);
99
+ }
36
100
  export {
101
+ apiRequestWithSelectedOrg,
102
+ createOrganizationFixture,
37
103
  createRoleFixture,
38
104
  createUserFixture,
105
+ deleteOrganizationIfExists,
39
106
  deleteRoleIfExists,
40
- deleteUserIfExists
107
+ deleteUserIfExists,
108
+ setRoleAclFeatures,
109
+ setUserAclVisibility
41
110
  };
42
111
  //# sourceMappingURL=authFixtures.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/helpers/integration/authFixtures.ts"],
4
- "sourcesContent": ["import { expect, type APIRequestContext } from '@playwright/test';\nimport { apiRequest } from './api';\nimport { expectId, readJsonSafe } from './generalFixtures';\n\nexport async function createRoleFixture(\n request: APIRequestContext,\n token: string,\n input: { name: string; tenantId?: string },\n): Promise<string> {\n const payload: { name: string; tenantId?: string } = {\n name: input.name,\n };\n if (typeof input.tenantId === 'string' && input.tenantId.length > 0) {\n payload.tenantId = input.tenantId;\n }\n const response = await apiRequest(request, 'POST', '/api/auth/roles', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/auth/roles should return 201').toBe(201);\n return expectId(body?.id, 'Role creation response should include id');\n}\n\nexport async function deleteRoleIfExists(\n request: APIRequestContext,\n token: string | null,\n roleId: string | null,\n): Promise<void> {\n if (!token || !roleId) return;\n await apiRequest(request, 'DELETE', `/api/auth/roles?id=${encodeURIComponent(roleId)}`, { token }).catch(() => undefined);\n}\n\nexport async function createUserFixture(\n request: APIRequestContext,\n token: string,\n input: { email: string; password: string; organizationId: string; roles: string[]; name?: string },\n): Promise<string> {\n const response = await apiRequest(request, 'POST', '/api/auth/users', {\n token,\n data: input,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/auth/users should return 201').toBe(201);\n return expectId(body?.id, 'User creation response should include id');\n}\n\nexport async function deleteUserIfExists(\n request: APIRequestContext,\n token: string | null,\n userId: string | null,\n): Promise<void> {\n if (!token || !userId) return;\n await apiRequest(request, 'DELETE', `/api/auth/users?id=${encodeURIComponent(userId)}`, { token }).catch(() => undefined);\n}\n"],
5
- "mappings": "AAAA,SAAS,cAAsC;AAC/C,SAAS,kBAAkB;AAC3B,SAAS,UAAU,oBAAoB;AAEvC,eAAsB,kBACpB,SACA,OACA,OACiB;AACjB,QAAM,UAA+C;AAAA,IACnD,MAAM,MAAM;AAAA,EACd;AACA,MAAI,OAAO,MAAM,aAAa,YAAY,MAAM,SAAS,SAAS,GAAG;AACnE,YAAQ,WAAW,MAAM;AAAA,EAC3B;AACA,QAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,mBAAmB;AAAA,IACpE;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AACD,QAAM,OAAO,MAAM,aAA8B,QAAQ;AACzD,SAAO,SAAS,OAAO,GAAG,wCAAwC,EAAE,KAAK,GAAG;AAC5E,SAAO,SAAS,MAAM,IAAI,0CAA0C;AACtE;AAEA,eAAsB,mBACpB,SACA,OACA,QACe;AACf,MAAI,CAAC,SAAS,CAAC,OAAQ;AACvB,QAAM,WAAW,SAAS,UAAU,sBAAsB,mBAAmB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,MAAM,MAAS;AAC1H;AAEA,eAAsB,kBACpB,SACA,OACA,OACiB;AACjB,QAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,mBAAmB;AAAA,IACpE;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AACD,QAAM,OAAO,MAAM,aAA8B,QAAQ;AACzD,SAAO,SAAS,OAAO,GAAG,wCAAwC,EAAE,KAAK,GAAG;AAC5E,SAAO,SAAS,MAAM,IAAI,0CAA0C;AACtE;AAEA,eAAsB,mBACpB,SACA,OACA,QACe;AACf,MAAI,CAAC,SAAS,CAAC,OAAQ;AACvB,QAAM,WAAW,SAAS,UAAU,sBAAsB,mBAAmB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,MAAM,MAAS;AAC1H;",
4
+ "sourcesContent": ["import { expect, type APIRequestContext } from '@playwright/test';\nimport { apiRequest } from './api';\nimport { expectId, readJsonSafe } from './generalFixtures';\n\nconst BASE_URL = process.env.BASE_URL?.trim() || null;\n\nfunction resolveUrl(path: string): string {\n return BASE_URL ? `${BASE_URL}${path}` : path;\n}\n\n/**\n * Variant of {@link apiRequest} that sets the `om_selected_org` cookie so the\n * server resolves `ctx.selectedOrganizationId` to a specific organization.\n * Create routes scope new records to that organization, which lets a test place\n * a fixture in an organization other than the caller's home org.\n */\nexport async function apiRequestWithSelectedOrg(\n request: APIRequestContext,\n method: string,\n path: string,\n options: { token: string; selectedOrgId: string; data?: unknown },\n) {\n const headers = {\n Authorization: `Bearer ${options.token}`,\n 'Content-Type': 'application/json',\n Cookie: `om_selected_org=${options.selectedOrgId}`,\n };\n return request.fetch(resolveUrl(path), { method, headers, data: options.data });\n}\n\nexport async function createRoleFixture(\n request: APIRequestContext,\n token: string,\n input: { name: string; tenantId?: string },\n): Promise<string> {\n const payload: { name: string; tenantId?: string } = {\n name: input.name,\n };\n if (typeof input.tenantId === 'string' && input.tenantId.length > 0) {\n payload.tenantId = input.tenantId;\n }\n const response = await apiRequest(request, 'POST', '/api/auth/roles', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/auth/roles should return 201').toBe(201);\n return expectId(body?.id, 'Role creation response should include id');\n}\n\nexport async function deleteRoleIfExists(\n request: APIRequestContext,\n token: string | null,\n roleId: string | null,\n): Promise<void> {\n if (!token || !roleId) return;\n await apiRequest(request, 'DELETE', `/api/auth/roles?id=${encodeURIComponent(roleId)}`, { token }).catch(() => undefined);\n}\n\nexport async function createUserFixture(\n request: APIRequestContext,\n token: string,\n input: { email: string; password: string; organizationId: string; roles: string[]; name?: string },\n): Promise<string> {\n const response = await apiRequest(request, 'POST', '/api/auth/users', {\n token,\n data: input,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/auth/users should return 201').toBe(201);\n return expectId(body?.id, 'User creation response should include id');\n}\n\nexport async function deleteUserIfExists(\n request: APIRequestContext,\n token: string | null,\n userId: string | null,\n): Promise<void> {\n if (!token || !userId) return;\n await apiRequest(request, 'DELETE', `/api/auth/users?id=${encodeURIComponent(userId)}`, { token }).catch(() => undefined);\n}\n\nexport async function createOrganizationFixture(\n request: APIRequestContext,\n token: string,\n input: { name: string; tenantId?: string },\n): Promise<string> {\n const payload: { name: string; tenantId?: string } = { name: input.name };\n if (typeof input.tenantId === 'string' && input.tenantId.length > 0) {\n payload.tenantId = input.tenantId;\n }\n const response = await apiRequest(request, 'POST', '/api/directory/organizations', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ id?: string }>(response);\n expect(response.status(), 'POST /api/directory/organizations should return 201').toBe(201);\n return expectId(body?.id, 'Organization creation response should include id');\n}\n\nexport async function deleteOrganizationIfExists(\n request: APIRequestContext,\n token: string | null,\n organizationId: string | null,\n): Promise<void> {\n if (!token || !organizationId) return;\n await apiRequest(request, 'DELETE', '/api/directory/organizations', {\n token,\n data: { id: organizationId },\n }).catch(() => undefined);\n}\n\nexport async function setRoleAclFeatures(\n request: APIRequestContext,\n token: string,\n input: { roleId: string; features: string[]; organizations?: string[] | null },\n): Promise<void> {\n const payload: { roleId: string; features: string[]; organizations?: string[] | null } = {\n roleId: input.roleId,\n features: input.features,\n };\n if (input.organizations !== undefined) {\n payload.organizations = input.organizations;\n }\n const response = await apiRequest(request, 'PUT', '/api/auth/roles/acl', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ ok?: boolean }>(response);\n expect(response.status(), 'PUT /api/auth/roles/acl should return 200').toBe(200);\n expect(body?.ok, 'Role ACL update should report ok=true').toBe(true);\n}\n\nexport async function setUserAclVisibility(\n request: APIRequestContext,\n token: string,\n input: { userId: string; organizations: string[] | null; features?: string[] },\n): Promise<void> {\n const payload: { userId: string; organizations: string[] | null; features?: string[] } = {\n userId: input.userId,\n organizations: input.organizations,\n };\n if (input.features !== undefined) {\n payload.features = input.features;\n }\n const response = await apiRequest(request, 'PUT', '/api/auth/users/acl', {\n token,\n data: payload,\n });\n const body = await readJsonSafe<{ ok?: boolean }>(response);\n expect(response.status(), 'PUT /api/auth/users/acl should return 200').toBe(200);\n expect(body?.ok, 'User ACL update should report ok=true').toBe(true);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,98 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { Client } from "pg";
4
+ function resolveAppRoot() {
5
+ const fromEnv = process.env.OM_TEST_APP_ROOT?.trim();
6
+ return fromEnv ? path.resolve(fromEnv) : path.resolve(process.cwd(), "apps/mercato");
7
+ }
8
+ function readEnvValue(key) {
9
+ if (process.env[key]) return process.env[key];
10
+ const candidatePaths = [
11
+ path.resolve(resolveAppRoot(), ".env"),
12
+ path.resolve(process.cwd(), "apps/mercato/.env"),
13
+ path.resolve(process.cwd(), ".env")
14
+ ];
15
+ for (const envPath of candidatePaths) {
16
+ try {
17
+ const content = readFileSync(envPath, "utf-8");
18
+ const match = content.match(new RegExp(`^${key}=(.+)$`, "m"));
19
+ if (match?.[1]) return match[1].trim();
20
+ } catch {
21
+ continue;
22
+ }
23
+ }
24
+ return void 0;
25
+ }
26
+ function resolveDatabaseUrl() {
27
+ const url = readEnvValue("DATABASE_URL");
28
+ if (!url) throw new Error("[internal] DATABASE_URL is not configured for integration DB fixtures");
29
+ return url;
30
+ }
31
+ async function withClient(run) {
32
+ const client = new Client({ connectionString: resolveDatabaseUrl() });
33
+ await client.connect();
34
+ try {
35
+ return await run(client);
36
+ } finally {
37
+ await client.end();
38
+ }
39
+ }
40
+ async function clearUserHomeOrganization(userId) {
41
+ await withClient(async (client) => {
42
+ await client.query("update users set organization_id = null where id = $1", [userId]);
43
+ });
44
+ }
45
+ async function setUserAclInDb(input) {
46
+ await withClient(async (client) => {
47
+ const existing = await client.query(
48
+ "select id from user_acls where user_id = $1 and tenant_id = $2 limit 1",
49
+ [input.userId, input.tenantId]
50
+ );
51
+ const featuresJson = JSON.stringify(input.features);
52
+ const organizationsJson = input.organizations === null ? null : JSON.stringify(input.organizations);
53
+ if (existing.rows.length > 0) {
54
+ await client.query(
55
+ "update user_acls set features_json = $2::jsonb, organizations_json = $3::jsonb, is_super_admin = false, updated_at = now() where id = $1",
56
+ [existing.rows[0].id, featuresJson, organizationsJson]
57
+ );
58
+ return;
59
+ }
60
+ await client.query(
61
+ `insert into user_acls (id, user_id, tenant_id, features_json, organizations_json, is_super_admin, created_at)
62
+ values (gen_random_uuid(), $1, $2, $3::jsonb, $4::jsonb, false, now())`,
63
+ [input.userId, input.tenantId, featuresJson, organizationsJson]
64
+ );
65
+ });
66
+ }
67
+ async function deleteUserAclInDb(userId) {
68
+ if (!userId) return;
69
+ await withClient(async (client) => {
70
+ await client.query("delete from user_acls where user_id = $1", [userId]);
71
+ });
72
+ }
73
+ async function createOrganizationInDb(input) {
74
+ return withClient(async (client) => {
75
+ const result = await client.query(
76
+ `insert into organizations
77
+ (id, tenant_id, name, is_active, ancestor_ids, child_ids, descendant_ids, depth, created_at, updated_at)
78
+ values (gen_random_uuid(), $1, $2, true, '[]'::jsonb, '[]'::jsonb, '[]'::jsonb, 0, now(), now())
79
+ returning id`,
80
+ [input.tenantId, input.name]
81
+ );
82
+ return result.rows[0].id;
83
+ });
84
+ }
85
+ async function deleteOrganizationInDb(organizationId) {
86
+ if (!organizationId) return;
87
+ await withClient(async (client) => {
88
+ await client.query("delete from organizations where id = $1", [organizationId]);
89
+ });
90
+ }
91
+ export {
92
+ clearUserHomeOrganization,
93
+ createOrganizationInDb,
94
+ deleteOrganizationInDb,
95
+ deleteUserAclInDb,
96
+ setUserAclInDb
97
+ };
98
+ //# sourceMappingURL=dbFixtures.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/helpers/integration/dbFixtures.ts"],
4
+ "sourcesContent": ["import { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { Client } from 'pg';\n\n/**\n * Database fixtures for the org-scope fail-open hardening tests.\n *\n * These helpers talk to Postgres directly via `pg` rather than bootstrapping the\n * app DI container, because:\n * - the directory create command denies non-super-admin actors (the only\n * loginable accounts here), so orgs cannot be created over the API; and\n * - granting `customers.*` through the ACL API requires a super-admin actor.\n * Raw SQL keeps the test self-contained and avoids depending on built package\n * `dist/` output for an in-process MikroORM bootstrap.\n */\n\nfunction resolveAppRoot(): string {\n const fromEnv = process.env.OM_TEST_APP_ROOT?.trim();\n return fromEnv ? path.resolve(fromEnv) : path.resolve(process.cwd(), 'apps/mercato');\n}\n\nfunction readEnvValue(key: string): string | undefined {\n if (process.env[key]) return process.env[key];\n const candidatePaths = [\n path.resolve(resolveAppRoot(), '.env'),\n path.resolve(process.cwd(), 'apps/mercato/.env'),\n path.resolve(process.cwd(), '.env'),\n ];\n for (const envPath of candidatePaths) {\n try {\n const content = readFileSync(envPath, 'utf-8');\n const match = content.match(new RegExp(`^${key}=(.+)$`, 'm'));\n if (match?.[1]) return match[1].trim();\n } catch {\n continue;\n }\n }\n return undefined;\n}\n\nfunction resolveDatabaseUrl(): string {\n const url = readEnvValue('DATABASE_URL');\n if (!url) throw new Error('[internal] DATABASE_URL is not configured for integration DB fixtures');\n return url;\n}\n\nasync function withClient<T>(run: (client: Client) => Promise<T>): Promise<T> {\n const client = new Client({ connectionString: resolveDatabaseUrl() });\n await client.connect();\n try {\n return await run(client);\n } finally {\n await client.end();\n }\n}\n\n/**\n * Nulls a user's home organization (`organization_id`) directly in the database.\n *\n * Required to construct the \"floating restricted user\" precondition: the JWT\n * `auth.orgId` is minted from `users.organization_id` at login, so this MUST run\n * BEFORE the user logs in.\n */\nexport async function clearUserHomeOrganization(userId: string): Promise<void> {\n await withClient(async (client) => {\n await client.query('update users set organization_id = null where id = $1', [userId]);\n });\n}\n\n/**\n * Upserts a per-user ACL row, setting the effective feature list and the\n * organization-visibility list.\n *\n * `organizations` maps to `OrganizationScope.allowedIds` (write path) and\n * `filterIds` (read path):\n * - `[orgA]` => restricted to orgA (write fail-open precondition, #2239)\n * - `[]` => restricted to zero orgs (read fail-open precondition, #2245)\n * - `null` => unrestricted\n */\nexport async function setUserAclInDb(input: {\n userId: string;\n tenantId: string;\n features: string[];\n organizations: string[] | null;\n}): Promise<void> {\n await withClient(async (client) => {\n const existing = await client.query<{ id: string }>(\n 'select id from user_acls where user_id = $1 and tenant_id = $2 limit 1',\n [input.userId, input.tenantId],\n );\n const featuresJson = JSON.stringify(input.features);\n const organizationsJson = input.organizations === null ? null : JSON.stringify(input.organizations);\n if (existing.rows.length > 0) {\n await client.query(\n 'update user_acls set features_json = $2::jsonb, organizations_json = $3::jsonb, is_super_admin = false, updated_at = now() where id = $1',\n [existing.rows[0].id, featuresJson, organizationsJson],\n );\n return;\n }\n await client.query(\n `insert into user_acls (id, user_id, tenant_id, features_json, organizations_json, is_super_admin, created_at)\n values (gen_random_uuid(), $1, $2, $3::jsonb, $4::jsonb, false, now())`,\n [input.userId, input.tenantId, featuresJson, organizationsJson],\n );\n });\n}\n\n/** Removes any per-user ACL rows for the user (best-effort test cleanup). */\nexport async function deleteUserAclInDb(userId: string): Promise<void> {\n if (!userId) return;\n await withClient(async (client) => {\n await client.query('delete from user_acls where user_id = $1', [userId]);\n });\n}\n\n/**\n * Creates an organization directly in the database within the given tenant.\n *\n * The directory create command routes through `enforceTenantSelection`, which\n * denies the (non-super-admin) accounts loginable on this instance. Landing the\n * org in a known tenant lets the floating user (created under it) share the\n * tenant \u2014 required because cross-tenant access returns 404 before the\n * org-scope guard ever runs.\n */\nexport async function createOrganizationInDb(input: { name: string; tenantId: string }): Promise<string> {\n return withClient(async (client) => {\n const result = await client.query<{ id: string }>(\n `insert into organizations\n (id, tenant_id, name, is_active, ancestor_ids, child_ids, descendant_ids, depth, created_at, updated_at)\n values (gen_random_uuid(), $1, $2, true, '[]'::jsonb, '[]'::jsonb, '[]'::jsonb, 0, now(), now())\n returning id`,\n [input.tenantId, input.name],\n );\n return result.rows[0].id;\n });\n}\n\n/** Hard-deletes an organization row (best-effort test cleanup). */\nexport async function deleteOrganizationInDb(organizationId: string | null): Promise<void> {\n if (!organizationId) return;\n await withClient(async (client) => {\n await client.query('delete from organizations where id = $1', [organizationId]);\n });\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,OAAO,UAAU;AACjB,SAAS,cAAc;AAcvB,SAAS,iBAAyB;AAChC,QAAM,UAAU,QAAQ,IAAI,kBAAkB,KAAK;AACnD,SAAO,UAAU,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,QAAQ,IAAI,GAAG,cAAc;AACrF;AAEA,SAAS,aAAa,KAAiC;AACrD,MAAI,QAAQ,IAAI,GAAG,EAAG,QAAO,QAAQ,IAAI,GAAG;AAC5C,QAAM,iBAAiB;AAAA,IACrB,KAAK,QAAQ,eAAe,GAAG,MAAM;AAAA,IACrC,KAAK,QAAQ,QAAQ,IAAI,GAAG,mBAAmB;AAAA,IAC/C,KAAK,QAAQ,QAAQ,IAAI,GAAG,MAAM;AAAA,EACpC;AACA,aAAW,WAAW,gBAAgB;AACpC,QAAI;AACF,YAAM,UAAU,aAAa,SAAS,OAAO;AAC7C,YAAM,QAAQ,QAAQ,MAAM,IAAI,OAAO,IAAI,GAAG,UAAU,GAAG,CAAC;AAC5D,UAAI,QAAQ,CAAC,EAAG,QAAO,MAAM,CAAC,EAAE,KAAK;AAAA,IACvC,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,qBAA6B;AACpC,QAAM,MAAM,aAAa,cAAc;AACvC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,uEAAuE;AACjG,SAAO;AACT;AAEA,eAAe,WAAc,KAAiD;AAC5E,QAAM,SAAS,IAAI,OAAO,EAAE,kBAAkB,mBAAmB,EAAE,CAAC;AACpE,QAAM,OAAO,QAAQ;AACrB,MAAI;AACF,WAAO,MAAM,IAAI,MAAM;AAAA,EACzB,UAAE;AACA,UAAM,OAAO,IAAI;AAAA,EACnB;AACF;AASA,eAAsB,0BAA0B,QAA+B;AAC7E,QAAM,WAAW,OAAO,WAAW;AACjC,UAAM,OAAO,MAAM,yDAAyD,CAAC,MAAM,CAAC;AAAA,EACtF,CAAC;AACH;AAYA,eAAsB,eAAe,OAKnB;AAChB,QAAM,WAAW,OAAO,WAAW;AACjC,UAAM,WAAW,MAAM,OAAO;AAAA,MAC5B;AAAA,MACA,CAAC,MAAM,QAAQ,MAAM,QAAQ;AAAA,IAC/B;AACA,UAAM,eAAe,KAAK,UAAU,MAAM,QAAQ;AAClD,UAAM,oBAAoB,MAAM,kBAAkB,OAAO,OAAO,KAAK,UAAU,MAAM,aAAa;AAClG,QAAI,SAAS,KAAK,SAAS,GAAG;AAC5B,YAAM,OAAO;AAAA,QACX;AAAA,QACA,CAAC,SAAS,KAAK,CAAC,EAAE,IAAI,cAAc,iBAAiB;AAAA,MACvD;AACA;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX;AAAA;AAAA,MAEA,CAAC,MAAM,QAAQ,MAAM,UAAU,cAAc,iBAAiB;AAAA,IAChE;AAAA,EACF,CAAC;AACH;AAGA,eAAsB,kBAAkB,QAA+B;AACrE,MAAI,CAAC,OAAQ;AACb,QAAM,WAAW,OAAO,WAAW;AACjC,UAAM,OAAO,MAAM,4CAA4C,CAAC,MAAM,CAAC;AAAA,EACzE,CAAC;AACH;AAWA,eAAsB,uBAAuB,OAA4D;AACvG,SAAO,WAAW,OAAO,WAAW;AAClC,UAAM,SAAS,MAAM,OAAO;AAAA,MAC1B;AAAA;AAAA;AAAA;AAAA,MAIA,CAAC,MAAM,UAAU,MAAM,IAAI;AAAA,IAC7B;AACA,WAAO,OAAO,KAAK,CAAC,EAAE;AAAA,EACxB,CAAC;AACH;AAGA,eAAsB,uBAAuB,gBAA8C;AACzF,MAAI,CAAC,eAAgB;AACrB,QAAM,WAAW,OAAO,WAAW;AACjC,UAAM,OAAO,MAAM,2CAA2C,CAAC,cAAc,CAAC;AAAA,EAChF,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -37,6 +37,7 @@ async function POST(req) {
37
37
  }
38
38
  const container = await createRequestContainer();
39
39
  const em = container.resolve("em");
40
+ const cache = ruleEngine.resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
40
41
  let eventBus = null;
41
42
  try {
42
43
  eventBus = container.resolve("eventBus");
@@ -76,7 +77,7 @@ async function POST(req) {
76
77
  return NextResponse.json({ error: `Invalid execution context: ${errors.join(", ")}` }, { status: 400 });
77
78
  }
78
79
  try {
79
- const result = await ruleEngine.executeRules(em, context, { eventBus });
80
+ const result = await ruleEngine.executeRules(em, context, { eventBus, cache });
80
81
  const response = {
81
82
  allowed: result.allowed,
82
83
  executedRules: result.executedRules.map((r) => ({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/business_rules/api/execute/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { EventBus } from '@open-mercato/events'\nimport { ruleEngineContextSchema } from '../../data/validators'\nimport * as ruleEngine from '../../lib/rule-engine'\n\nconst executeRequestSchema = z.object({\n entityType: z.string().min(1, 'entityType is required'),\n entityId: z.string().optional(),\n eventType: z.string().optional(),\n data: z.any(),\n dryRun: z.boolean().optional().default(false),\n})\n\nconst executeResponseSchema = z.object({\n allowed: z.boolean(),\n executedRules: z.array(z.object({\n ruleId: z.string(),\n ruleName: z.string(),\n conditionResult: z.boolean(),\n executionTime: z.number(),\n error: z.string().optional(),\n })),\n totalExecutionTime: z.number(),\n errors: z.array(z.string()).optional(),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['business_rules.execute'] },\n}\n\nexport const metadata = routeMetadata\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n let eventBus: EventBus | null = null\n try {\n eventBus = container.resolve('eventBus') as EventBus\n } catch {\n eventBus = null\n }\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const parsed = executeRequestSchema.safeParse(body)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const { entityType, entityId, eventType, data, dryRun } = parsed.data\n\n const context: ruleEngine.RuleEngineContext = {\n entityType,\n entityId,\n eventType,\n data,\n user: {\n id: auth.sub,\n email: auth.email,\n role: (auth.role as string) ?? undefined,\n },\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? '',\n executedBy: auth.sub ?? auth.email ?? null,\n dryRun,\n }\n\n const validation = ruleEngineContextSchema.safeParse(context)\n if (!validation.success) {\n const errors = validation.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Invalid execution context: ${errors.join(', ')}` }, { status: 400 })\n }\n\n try {\n const result = await ruleEngine.executeRules(em, context, { eventBus })\n\n const response = {\n allowed: result.allowed,\n executedRules: result.executedRules.map(r => ({\n ruleId: r.rule.ruleId,\n ruleName: r.rule.ruleName,\n ruleType: r.rule.ruleType,\n conditionResult: r.conditionResult,\n actionsExecuted: r.actionsExecuted ? {\n success: r.actionsExecuted.success,\n results: r.actionsExecuted.results.map(ar => ({\n type: ar.action.type,\n success: ar.success,\n error: ar.error,\n })),\n } : null,\n executionTime: r.executionTime,\n error: r.error,\n logId: r.logId ? String(r.logId) : undefined,\n })),\n totalExecutionTime: result.totalExecutionTime,\n errors: result.errors,\n logIds: result.logIds?.map((entry) => String(entry)),\n }\n\n return NextResponse.json(response, { status: 200 })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return NextResponse.json(\n { error: `Rule execution failed: ${errorMessage}` },\n { status: 500 }\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Business Rules',\n summary: 'Execute business rules',\n methods: {\n POST: {\n summary: 'Execute rules for given context',\n description: 'Manually executes applicable business rules for the specified entity type, event, and data. Supports dry-run mode to test rules without executing actions.',\n requestBody: {\n contentType: 'application/json',\n schema: executeRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Rules executed successfully',\n schema: executeResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid request payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 500, description: 'Execution error', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { EventBus } from '@open-mercato/events'\nimport { ruleEngineContextSchema } from '../../data/validators'\nimport * as ruleEngine from '../../lib/rule-engine'\n\nconst executeRequestSchema = z.object({\n entityType: z.string().min(1, 'entityType is required'),\n entityId: z.string().optional(),\n eventType: z.string().optional(),\n data: z.any(),\n dryRun: z.boolean().optional().default(false),\n})\n\nconst executeResponseSchema = z.object({\n allowed: z.boolean(),\n executedRules: z.array(z.object({\n ruleId: z.string(),\n ruleName: z.string(),\n conditionResult: z.boolean(),\n executionTime: z.number(),\n error: z.string().optional(),\n })),\n totalExecutionTime: z.number(),\n errors: z.array(z.string()).optional(),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nconst routeMetadata = {\n POST: { requireAuth: true, requireFeatures: ['business_rules.execute'] },\n}\n\nexport const metadata = routeMetadata\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = ruleEngine.resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n let eventBus: EventBus | null = null\n try {\n eventBus = container.resolve('eventBus') as EventBus\n } catch {\n eventBus = null\n }\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const parsed = executeRequestSchema.safeParse(body)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const { entityType, entityId, eventType, data, dryRun } = parsed.data\n\n const context: ruleEngine.RuleEngineContext = {\n entityType,\n entityId,\n eventType,\n data,\n user: {\n id: auth.sub,\n email: auth.email,\n role: (auth.role as string) ?? undefined,\n },\n tenantId: auth.tenantId ?? '',\n organizationId: auth.orgId ?? '',\n executedBy: auth.sub ?? auth.email ?? null,\n dryRun,\n }\n\n const validation = ruleEngineContextSchema.safeParse(context)\n if (!validation.success) {\n const errors = validation.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Invalid execution context: ${errors.join(', ')}` }, { status: 400 })\n }\n\n try {\n const result = await ruleEngine.executeRules(em, context, { eventBus, cache })\n\n const response = {\n allowed: result.allowed,\n executedRules: result.executedRules.map(r => ({\n ruleId: r.rule.ruleId,\n ruleName: r.rule.ruleName,\n ruleType: r.rule.ruleType,\n conditionResult: r.conditionResult,\n actionsExecuted: r.actionsExecuted ? {\n success: r.actionsExecuted.success,\n results: r.actionsExecuted.results.map(ar => ({\n type: ar.action.type,\n success: ar.success,\n error: ar.error,\n })),\n } : null,\n executionTime: r.executionTime,\n error: r.error,\n logId: r.logId ? String(r.logId) : undefined,\n })),\n totalExecutionTime: result.totalExecutionTime,\n errors: result.errors,\n logIds: result.logIds?.map((entry) => String(entry)),\n }\n\n return NextResponse.json(response, { status: 200 })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return NextResponse.json(\n { error: `Rule execution failed: ${errorMessage}` },\n { status: 500 }\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Business Rules',\n summary: 'Execute business rules',\n methods: {\n POST: {\n summary: 'Execute rules for given context',\n description: 'Manually executes applicable business rules for the specified entity type, event, and data. Supports dry-run mode to test rules without executing actions.',\n requestBody: {\n contentType: 'application/json',\n schema: executeRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Rules executed successfully',\n schema: executeResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid request payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 500, description: 'Execution error', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -12,6 +12,10 @@ import {
12
12
  createLocalizedUpdateBusinessRuleSchema,
13
13
  ruleTypeSchema
14
14
  } from "../../data/validators.js";
15
+ import {
16
+ invalidateBusinessRuleDiscoveryCache,
17
+ resolveBusinessRuleDiscoveryCache
18
+ } from "../../lib/rule-engine.js";
15
19
  const querySchema = z.looseObject({
16
20
  id: z.uuid().optional(),
17
21
  page: z.coerce.number().min(1).default(1),
@@ -154,6 +158,7 @@ async function POST(req) {
154
158
  }
155
159
  const container = await createRequestContainer();
156
160
  const em = container.resolve("em");
161
+ const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
157
162
  let body;
158
163
  try {
159
164
  body = await req.json();
@@ -180,6 +185,7 @@ async function POST(req) {
180
185
  const rule = em.create(BusinessRule, data);
181
186
  try {
182
187
  await em.persist(rule).flush();
188
+ await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId);
183
189
  } catch (error) {
184
190
  console.error("[business_rules.rules] Failed to persist new rule:", error);
185
191
  return NextResponse.json(
@@ -196,6 +202,7 @@ async function PUT(req) {
196
202
  }
197
203
  const container = await createRequestContainer();
198
204
  const em = container.resolve("em");
205
+ const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
199
206
  let body;
200
207
  try {
201
208
  body = await req.json();
@@ -231,6 +238,7 @@ async function PUT(req) {
231
238
  em.assign(rule, parsed.data);
232
239
  try {
233
240
  await em.persist(rule).flush();
241
+ await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId);
234
242
  } catch (error) {
235
243
  console.error("[business_rules.rules] Failed to persist rule update:", error);
236
244
  return NextResponse.json(
@@ -252,6 +260,7 @@ async function DELETE(req) {
252
260
  }
253
261
  const container = await createRequestContainer();
254
262
  const em = container.resolve("em");
263
+ const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container));
255
264
  const rule = await em.findOne(BusinessRule, {
256
265
  id,
257
266
  tenantId: auth.tenantId,
@@ -263,6 +272,7 @@ async function DELETE(req) {
263
272
  }
264
273
  rule.deletedAt = /* @__PURE__ */ new Date();
265
274
  await em.persist(rule).flush();
275
+ await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId);
266
276
  return NextResponse.json({ ok: true });
267
277
  }
268
278
  const openApi = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/business_rules/api/rules/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { BusinessRule } from '../../data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport {\n createBusinessRuleSchema,\n updateBusinessRuleSchema,\n createLocalizedBusinessRuleSchema,\n createLocalizedUpdateBusinessRuleSchema,\n businessRuleFilterSchema,\n ruleTypeSchema,\n} from '../../data/validators'\n\nconst querySchema = z.looseObject({\n id: z.uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n ruleId: z.string().optional(),\n ruleType: ruleTypeSchema.optional(),\n entityType: z.string().optional(),\n eventType: z.string().optional(),\n enabled: z.coerce.boolean().optional(),\n ruleCategory: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional().default('desc'),\n})\n\nconst ruleListItemSchema = z.object({\n id: z.string().uuid(),\n ruleId: z.string(),\n ruleName: z.string(),\n description: z.string().nullable(),\n ruleType: ruleTypeSchema,\n ruleCategory: z.string().nullable(),\n entityType: z.string(),\n eventType: z.string().nullable(),\n enabled: z.boolean(),\n priority: z.number(),\n version: z.number(),\n effectiveFrom: z.string().nullable(),\n effectiveTo: z.string().nullable(),\n tenantId: z.string().uuid(),\n organizationId: z.string().uuid(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst ruleListResponseSchema = z.object({\n items: z.array(ruleListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n})\n\nconst ruleCreateResponseSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst okResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['business_rules.view'] },\n POST: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n ruleId: url.searchParams.get('ruleId') || undefined,\n ruleType: url.searchParams.get('ruleType') || undefined,\n entityType: url.searchParams.get('entityType') || undefined,\n eventType: url.searchParams.get('eventType') || undefined,\n enabled: url.searchParams.get('enabled') || undefined,\n ruleCategory: url.searchParams.get('ruleCategory') || undefined,\n sortField: url.searchParams.get('sortField') || undefined,\n sortDir: url.searchParams.get('sortDir') || undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query parameters' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const { id, page, pageSize, search, ruleId, ruleType, entityType, eventType, enabled, ruleCategory, sortField, sortDir } = parsed.data\n\n const filters: Record<string, any> = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n }\n\n if (id) filters.id = id\n if (ruleId) filters.ruleId = { $ilike: `%${escapeLikePattern(ruleId)}%` }\n if (search) filters.ruleName = { $ilike: `%${escapeLikePattern(search)}%` }\n if (ruleType) filters.ruleType = ruleType\n if (entityType) filters.entityType = entityType\n if (eventType) filters.eventType = eventType\n if (enabled !== undefined) filters.enabled = enabled\n if (ruleCategory) filters.ruleCategory = ruleCategory\n\n const sortFieldMap: Record<string, string> = {\n ruleId: 'ruleId',\n ruleName: 'ruleName',\n ruleType: 'ruleType',\n entityType: 'entityType',\n priority: 'priority',\n createdAt: 'createdAt',\n updatedAt: 'updatedAt',\n }\n\n const orderByField = sortField && sortFieldMap[sortField] ? sortFieldMap[sortField] : 'priority'\n const orderBy = { [orderByField]: sortDir, ruleId: 'asc' as const }\n\n const [rows, count] = await em.findAndCount(\n BusinessRule,\n filters,\n {\n limit: pageSize,\n offset: (page - 1) * pageSize,\n orderBy,\n }\n )\n\n const items = rows.map((rule) => ({\n id: rule.id,\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n description: rule.description ?? null,\n ruleType: rule.ruleType,\n ruleCategory: rule.ruleCategory ?? null,\n entityType: rule.entityType,\n eventType: rule.eventType ?? null,\n enabled: rule.enabled,\n priority: rule.priority,\n version: rule.version,\n effectiveFrom: rule.effectiveFrom ? rule.effectiveFrom.toISOString() : null,\n effectiveTo: rule.effectiveTo ? rule.effectiveTo.toISOString() : null,\n tenantId: rule.tenantId,\n organizationId: rule.organizationId,\n createdAt: rule.createdAt.toISOString(),\n updatedAt: rule.updatedAt.toISOString(),\n }))\n\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n\n return NextResponse.json({ items, total: count, totalPages })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const payload = {\n ...body,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n createdBy: auth.sub ?? auth.email ?? null,\n }\n\n const { t } = await resolveTranslations()\n const schema = createLocalizedBusinessRuleSchema(t)\n const parsed = schema.safeParse(payload)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const data = {\n ...parsed.data,\n conditionExpression: parsed.data.conditionExpression ?? null,\n }\n\n const rule = em.create(BusinessRule, data)\n\n try {\n await em.persist(rule).flush()\n } catch (error) {\n console.error('[business_rules.rules] Failed to persist new rule:', error)\n return NextResponse.json(\n { error: t('business_rules.errors.createFailed') },\n { status: 500 },\n )\n }\n\n return NextResponse.json({ id: rule.id }, { status: 201 })\n}\n\nexport async function PUT(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n if (!body.id) {\n return NextResponse.json({ error: 'Rule id is required' }, { status: 400 })\n }\n\n const payload = {\n ...body,\n updatedBy: auth.sub ?? auth.email ?? null,\n }\n delete (payload as Record<string, unknown>).tenantId\n delete (payload as Record<string, unknown>).organizationId\n delete (payload as Record<string, unknown>).createdBy\n\n const { t } = await resolveTranslations()\n const schema = createLocalizedUpdateBusinessRuleSchema(t)\n const parsed = schema.safeParse(payload)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const rule = await em.findOne(BusinessRule, {\n id: parsed.data.id,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n })\n\n if (!rule) {\n return NextResponse.json({ error: 'Rule not found' }, { status: 404 })\n }\n\n em.assign(rule, parsed.data)\n\n try {\n await em.persist(rule).flush()\n } catch (error) {\n console.error('[business_rules.rules] Failed to persist rule update:', error)\n return NextResponse.json(\n { error: t('business_rules.errors.updateFailed') },\n { status: 500 },\n )\n }\n\n return NextResponse.json({ ok: true })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const id = url.searchParams.get('id')\n\n if (!id) {\n return NextResponse.json({ error: 'Rule id is required' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const rule = await em.findOne(BusinessRule, {\n id,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n })\n\n if (!rule) {\n return NextResponse.json({ error: 'Rule not found' }, { status: 404 })\n }\n\n rule.deletedAt = new Date()\n await em.persist(rule).flush()\n\n return NextResponse.json({ ok: true })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Business Rules',\n summary: 'Business rule management',\n methods: {\n GET: {\n summary: 'List business rules',\n description: 'Returns business rules for the current tenant and organization with filtering and pagination.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Business rules collection', schema: ruleListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid query parameters', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n ],\n },\n POST: {\n summary: 'Create business rule',\n description: 'Creates a new business rule for the current tenant and organization.',\n requestBody: {\n contentType: 'application/json',\n schema: createBusinessRuleSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Business rule created',\n schema: ruleCreateResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update business rule',\n description: 'Updates an existing business rule.',\n requestBody: {\n contentType: 'application/json',\n schema: updateBusinessRuleSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Business rule updated',\n schema: okResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'Business rule not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete business rule',\n description: 'Soft deletes a business rule by identifier.',\n query: z.object({ id: z.string().uuid().describe('Business rule identifier') }),\n responses: [\n { status: 200, description: 'Business rule deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid identifier', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'Business rule not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { BusinessRule } from '../../data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport {\n createBusinessRuleSchema,\n updateBusinessRuleSchema,\n createLocalizedBusinessRuleSchema,\n createLocalizedUpdateBusinessRuleSchema,\n businessRuleFilterSchema,\n ruleTypeSchema,\n} from '../../data/validators'\nimport {\n invalidateBusinessRuleDiscoveryCache,\n resolveBusinessRuleDiscoveryCache,\n} from '../../lib/rule-engine'\n\nconst querySchema = z.looseObject({\n id: z.uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n ruleId: z.string().optional(),\n ruleType: ruleTypeSchema.optional(),\n entityType: z.string().optional(),\n eventType: z.string().optional(),\n enabled: z.coerce.boolean().optional(),\n ruleCategory: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional().default('desc'),\n})\n\nconst ruleListItemSchema = z.object({\n id: z.string().uuid(),\n ruleId: z.string(),\n ruleName: z.string(),\n description: z.string().nullable(),\n ruleType: ruleTypeSchema,\n ruleCategory: z.string().nullable(),\n entityType: z.string(),\n eventType: z.string().nullable(),\n enabled: z.boolean(),\n priority: z.number(),\n version: z.number(),\n effectiveFrom: z.string().nullable(),\n effectiveTo: z.string().nullable(),\n tenantId: z.string().uuid(),\n organizationId: z.string().uuid(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\n\nconst ruleListResponseSchema = z.object({\n items: z.array(ruleListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n})\n\nconst ruleCreateResponseSchema = z.object({\n id: z.string().uuid(),\n})\n\nconst okResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nconst errorResponseSchema = z.object({\n error: z.string(),\n})\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['business_rules.view'] },\n POST: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['business_rules.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n ruleId: url.searchParams.get('ruleId') || undefined,\n ruleType: url.searchParams.get('ruleType') || undefined,\n entityType: url.searchParams.get('entityType') || undefined,\n eventType: url.searchParams.get('eventType') || undefined,\n enabled: url.searchParams.get('enabled') || undefined,\n ruleCategory: url.searchParams.get('ruleCategory') || undefined,\n sortField: url.searchParams.get('sortField') || undefined,\n sortDir: url.searchParams.get('sortDir') || undefined,\n })\n\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid query parameters' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n\n const { id, page, pageSize, search, ruleId, ruleType, entityType, eventType, enabled, ruleCategory, sortField, sortDir } = parsed.data\n\n const filters: Record<string, any> = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n }\n\n if (id) filters.id = id\n if (ruleId) filters.ruleId = { $ilike: `%${escapeLikePattern(ruleId)}%` }\n if (search) filters.ruleName = { $ilike: `%${escapeLikePattern(search)}%` }\n if (ruleType) filters.ruleType = ruleType\n if (entityType) filters.entityType = entityType\n if (eventType) filters.eventType = eventType\n if (enabled !== undefined) filters.enabled = enabled\n if (ruleCategory) filters.ruleCategory = ruleCategory\n\n const sortFieldMap: Record<string, string> = {\n ruleId: 'ruleId',\n ruleName: 'ruleName',\n ruleType: 'ruleType',\n entityType: 'entityType',\n priority: 'priority',\n createdAt: 'createdAt',\n updatedAt: 'updatedAt',\n }\n\n const orderByField = sortField && sortFieldMap[sortField] ? sortFieldMap[sortField] : 'priority'\n const orderBy = { [orderByField]: sortDir, ruleId: 'asc' as const }\n\n const [rows, count] = await em.findAndCount(\n BusinessRule,\n filters,\n {\n limit: pageSize,\n offset: (page - 1) * pageSize,\n orderBy,\n }\n )\n\n const items = rows.map((rule) => ({\n id: rule.id,\n ruleId: rule.ruleId,\n ruleName: rule.ruleName,\n description: rule.description ?? null,\n ruleType: rule.ruleType,\n ruleCategory: rule.ruleCategory ?? null,\n entityType: rule.entityType,\n eventType: rule.eventType ?? null,\n enabled: rule.enabled,\n priority: rule.priority,\n version: rule.version,\n effectiveFrom: rule.effectiveFrom ? rule.effectiveFrom.toISOString() : null,\n effectiveTo: rule.effectiveTo ? rule.effectiveTo.toISOString() : null,\n tenantId: rule.tenantId,\n organizationId: rule.organizationId,\n createdAt: rule.createdAt.toISOString(),\n updatedAt: rule.updatedAt.toISOString(),\n }))\n\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n\n return NextResponse.json({ items, total: count, totalPages })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n const payload = {\n ...body,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n createdBy: auth.sub ?? auth.email ?? null,\n }\n\n const { t } = await resolveTranslations()\n const schema = createLocalizedBusinessRuleSchema(t)\n const parsed = schema.safeParse(payload)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const data = {\n ...parsed.data,\n conditionExpression: parsed.data.conditionExpression ?? null,\n }\n\n const rule = em.create(BusinessRule, data)\n\n try {\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n } catch (error) {\n console.error('[business_rules.rules] Failed to persist new rule:', error)\n return NextResponse.json(\n { error: t('business_rules.errors.createFailed') },\n { status: 500 },\n )\n }\n\n return NextResponse.json({ id: rule.id }, { status: 201 })\n}\n\nexport async function PUT(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n let body: any\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n\n if (!body.id) {\n return NextResponse.json({ error: 'Rule id is required' }, { status: 400 })\n }\n\n const payload = {\n ...body,\n updatedBy: auth.sub ?? auth.email ?? null,\n }\n delete (payload as Record<string, unknown>).tenantId\n delete (payload as Record<string, unknown>).organizationId\n delete (payload as Record<string, unknown>).createdBy\n\n const { t } = await resolveTranslations()\n const schema = createLocalizedUpdateBusinessRuleSchema(t)\n const parsed = schema.safeParse(payload)\n if (!parsed.success) {\n const errors = parsed.error.issues.map(e => `${e.path.join('.')}: ${e.message}`)\n return NextResponse.json({ error: `Validation failed: ${errors.join(', ')}` }, { status: 400 })\n }\n\n const rule = await em.findOne(BusinessRule, {\n id: parsed.data.id,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n })\n\n if (!rule) {\n return NextResponse.json({ error: 'Rule not found' }, { status: 404 })\n }\n\n em.assign(rule, parsed.data)\n\n try {\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n } catch (error) {\n console.error('[business_rules.rules] Failed to persist rule update:', error)\n return NextResponse.json(\n { error: t('business_rules.errors.updateFailed') },\n { status: 500 },\n )\n }\n\n return NextResponse.json({ ok: true })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const url = new URL(req.url)\n const id = url.searchParams.get('id')\n\n if (!id) {\n return NextResponse.json({ error: 'Rule id is required' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const cache = resolveBusinessRuleDiscoveryCache(container.resolve.bind(container))\n\n const rule = await em.findOne(BusinessRule, {\n id,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n })\n\n if (!rule) {\n return NextResponse.json({ error: 'Rule not found' }, { status: 404 })\n }\n\n rule.deletedAt = new Date()\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)\n\n return NextResponse.json({ ok: true })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Business Rules',\n summary: 'Business rule management',\n methods: {\n GET: {\n summary: 'List business rules',\n description: 'Returns business rules for the current tenant and organization with filtering and pagination.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Business rules collection', schema: ruleListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid query parameters', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n ],\n },\n POST: {\n summary: 'Create business rule',\n description: 'Creates a new business rule for the current tenant and organization.',\n requestBody: {\n contentType: 'application/json',\n schema: createBusinessRuleSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Business rule created',\n schema: ruleCreateResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update business rule',\n description: 'Updates an existing business rule.',\n requestBody: {\n contentType: 'application/json',\n schema: updateBusinessRuleSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Business rule updated',\n schema: okResponseSchema,\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'Business rule not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete business rule',\n description: 'Soft deletes a business rule by identifier.',\n query: z.object({ id: z.string().uuid().describe('Business rule identifier') }),\n responses: [\n { status: 200, description: 'Business rule deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid identifier', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'Business rule not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,9 @@
1
1
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
2
2
  import { BusinessRule } from "./data/entities.js";
3
+ import {
4
+ invalidateBusinessRuleDiscoveryCache,
5
+ resolveBusinessRuleDiscoveryCache
6
+ } from "./lib/rule-engine.js";
3
7
  import * as fs from "fs";
4
8
  import * as path from "path";
5
9
  function parseArgs(args) {
@@ -27,6 +31,7 @@ const seedGuardRules = {
27
31
  try {
28
32
  const { resolve } = await createRequestContainer();
29
33
  const em = resolve("em");
34
+ const cache = resolveBusinessRuleDiscoveryCache(resolve);
30
35
  const rulesPath = path.join(__dirname, "../workflows/examples", "guard-rules-example.json");
31
36
  const rulesData = JSON.parse(fs.readFileSync(rulesPath, "utf8"));
32
37
  console.log("\u{1F9E0} Seeding guard rules...");
@@ -49,6 +54,7 @@ const seedGuardRules = {
49
54
  organizationId
50
55
  });
51
56
  await em.persist(rule).flush();
57
+ await invalidateBusinessRuleDiscoveryCache(cache, tenantId, organizationId);
52
58
  console.log(` \u2713 Seeded guard rule: ${rule.ruleName}`);
53
59
  seededCount++;
54
60
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/business_rules/cli.ts"],
4
- "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { BusinessRule } from './data/entities'\nimport * as fs from 'fs'\nimport * as path from 'path'\n\n/**\n * Parse CLI arguments\n */\nfunction parseArgs(args: string[]) {\n const result: Record<string, string> = {}\n for (let i = 0; i < args.length; i += 2) {\n const key = args[i]?.replace(/^-+/, '')\n const value = args[i + 1]\n if (key && value) {\n result[key] = value\n }\n }\n return result\n}\n\n/**\n * Seed guard rules for workflow checkout demo\n */\nconst seedGuardRules: ModuleCli = {\n command: 'seed-guard-rules',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato business_rules seed-guard-rules --tenant <tenantId> --org <organizationId>')\n console.error(' or: mercato business_rules seed-guard-rules -t <tenantId> -o <organizationId>')\n return\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Read guard rules from workflows examples\n const rulesPath = path.join(__dirname, '../workflows/examples', 'guard-rules-example.json')\n const rulesData = JSON.parse(fs.readFileSync(rulesPath, 'utf8'))\n\n console.log('\uD83E\uDDE0 Seeding guard rules...')\n let seededCount = 0\n let skippedCount = 0\n\n for (const ruleData of rulesData) {\n // Check if rule already exists\n const existing = await em.findOne(BusinessRule, {\n ruleId: ruleData.ruleId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(` \u2298 Guard rule '${ruleData.ruleId}' already exists`)\n skippedCount++\n continue\n }\n\n // Create the business rule\n const rule = em.create(BusinessRule, {\n ...ruleData,\n tenantId,\n organizationId,\n })\n\n await em.persist(rule).flush()\n console.log(` \u2713 Seeded guard rule: ${rule.ruleName}`)\n seededCount++\n }\n\n console.log(`\\n\u2705 Guard rules seeding complete:`)\n console.log(` - Seeded: ${seededCount}`)\n console.log(` - Skipped (existing): ${skippedCount}`)\n console.log(` - Total: ${rulesData.length}`)\n } catch (error) {\n console.error('Error seeding guard rules:', error)\n throw error\n }\n },\n}\n\nconst businessRulesCliCommands = [\n seedGuardRules,\n]\n\nexport default businessRulesCliCommands\n"],
5
- "mappings": "AACA,SAAS,8BAA8B;AAEvC,SAAS,oBAAoB;AAC7B,YAAY,QAAQ;AACpB,YAAY,UAAU;AAKtB,SAAS,UAAU,MAAgB;AACjC,QAAM,SAAiC,CAAC;AACxC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,MAAM,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACtC,UAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,QAAI,OAAO,OAAO;AAChB,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAKA,MAAM,iBAA4B;AAAA,EAChC,SAAS;AAAA,EACT,MAAM,IAAI,MAAgB;AACxB,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,WAAW,OAAO,KAAK,YAAY,KAAK,UAAU,KAAK,KAAK,EAAE;AACpE,UAAM,iBAAiB,OAAO,KAAK,kBAAkB,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK,EAAE;AAE3F,QAAI,CAAC,YAAY,CAAC,gBAAgB;AAChC,cAAQ,MAAM,2FAA2F;AACzG,cAAQ,MAAM,kFAAkF;AAChG;AAAA,IACF;AAEA,QAAI;AACF,YAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,YAAM,KAAK,QAAuB,IAAI;AAGtC,YAAM,YAAY,KAAK,KAAK,WAAW,yBAAyB,0BAA0B;AAC1F,YAAM,YAAY,KAAK,MAAM,GAAG,aAAa,WAAW,MAAM,CAAC;AAE/D,cAAQ,IAAI,kCAA2B;AACvC,UAAI,cAAc;AAClB,UAAI,eAAe;AAEnB,iBAAW,YAAY,WAAW;AAEhC,cAAM,WAAW,MAAM,GAAG,QAAQ,cAAc;AAAA,UAC9C,QAAQ,SAAS;AAAA,UACjB;AAAA,UACA;AAAA,QACF,CAAC;AAED,YAAI,UAAU;AACZ,kBAAQ,IAAI,wBAAmB,SAAS,MAAM,kBAAkB;AAChE;AACA;AAAA,QACF;AAGA,cAAM,OAAO,GAAG,OAAO,cAAc;AAAA,UACnC,GAAG;AAAA,UACH;AAAA,UACA;AAAA,QACF,CAAC;AAED,cAAM,GAAG,QAAQ,IAAI,EAAE,MAAM;AAC7B,gBAAQ,IAAI,+BAA0B,KAAK,QAAQ,EAAE;AACrD;AAAA,MACF;AAEA,cAAQ,IAAI;AAAA,qCAAmC;AAC/C,cAAQ,IAAI,eAAe,WAAW,EAAE;AACxC,cAAQ,IAAI,2BAA2B,YAAY,EAAE;AACrD,cAAQ,IAAI,cAAc,UAAU,MAAM,EAAE;AAAA,IAC9C,SAAS,OAAO;AACd,cAAQ,MAAM,8BAA8B,KAAK;AACjD,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,MAAM,2BAA2B;AAAA,EAC/B;AACF;AAEA,IAAO,cAAQ;",
4
+ "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { BusinessRule } from './data/entities'\nimport {\n invalidateBusinessRuleDiscoveryCache,\n resolveBusinessRuleDiscoveryCache,\n} from './lib/rule-engine'\nimport * as fs from 'fs'\nimport * as path from 'path'\n\n/**\n * Parse CLI arguments\n */\nfunction parseArgs(args: string[]) {\n const result: Record<string, string> = {}\n for (let i = 0; i < args.length; i += 2) {\n const key = args[i]?.replace(/^-+/, '')\n const value = args[i + 1]\n if (key && value) {\n result[key] = value\n }\n }\n return result\n}\n\n/**\n * Seed guard rules for workflow checkout demo\n */\nconst seedGuardRules: ModuleCli = {\n command: 'seed-guard-rules',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato business_rules seed-guard-rules --tenant <tenantId> --org <organizationId>')\n console.error(' or: mercato business_rules seed-guard-rules -t <tenantId> -o <organizationId>')\n return\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const cache = resolveBusinessRuleDiscoveryCache(resolve)\n\n // Read guard rules from workflows examples\n const rulesPath = path.join(__dirname, '../workflows/examples', 'guard-rules-example.json')\n const rulesData = JSON.parse(fs.readFileSync(rulesPath, 'utf8'))\n\n console.log('\uD83E\uDDE0 Seeding guard rules...')\n let seededCount = 0\n let skippedCount = 0\n\n for (const ruleData of rulesData) {\n // Check if rule already exists\n const existing = await em.findOne(BusinessRule, {\n ruleId: ruleData.ruleId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(` \u2298 Guard rule '${ruleData.ruleId}' already exists`)\n skippedCount++\n continue\n }\n\n // Create the business rule\n const rule = em.create(BusinessRule, {\n ...ruleData,\n tenantId,\n organizationId,\n })\n\n await em.persist(rule).flush()\n await invalidateBusinessRuleDiscoveryCache(cache, tenantId, organizationId)\n console.log(` \u2713 Seeded guard rule: ${rule.ruleName}`)\n seededCount++\n }\n\n console.log(`\\n\u2705 Guard rules seeding complete:`)\n console.log(` - Seeded: ${seededCount}`)\n console.log(` - Skipped (existing): ${skippedCount}`)\n console.log(` - Total: ${rulesData.length}`)\n } catch (error) {\n console.error('Error seeding guard rules:', error)\n throw error\n }\n },\n}\n\nconst businessRulesCliCommands = [\n seedGuardRules,\n]\n\nexport default businessRulesCliCommands\n"],
5
+ "mappings": "AACA,SAAS,8BAA8B;AAEvC,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,YAAY,QAAQ;AACpB,YAAY,UAAU;AAKtB,SAAS,UAAU,MAAgB;AACjC,QAAM,SAAiC,CAAC;AACxC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,MAAM,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACtC,UAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,QAAI,OAAO,OAAO;AAChB,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAKA,MAAM,iBAA4B;AAAA,EAChC,SAAS;AAAA,EACT,MAAM,IAAI,MAAgB;AACxB,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,WAAW,OAAO,KAAK,YAAY,KAAK,UAAU,KAAK,KAAK,EAAE;AACpE,UAAM,iBAAiB,OAAO,KAAK,kBAAkB,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK,EAAE;AAE3F,QAAI,CAAC,YAAY,CAAC,gBAAgB;AAChC,cAAQ,MAAM,2FAA2F;AACzG,cAAQ,MAAM,kFAAkF;AAChG;AAAA,IACF;AAEA,QAAI;AACF,YAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,YAAM,KAAK,QAAuB,IAAI;AACtC,YAAM,QAAQ,kCAAkC,OAAO;AAGvD,YAAM,YAAY,KAAK,KAAK,WAAW,yBAAyB,0BAA0B;AAC1F,YAAM,YAAY,KAAK,MAAM,GAAG,aAAa,WAAW,MAAM,CAAC;AAE/D,cAAQ,IAAI,kCAA2B;AACvC,UAAI,cAAc;AAClB,UAAI,eAAe;AAEnB,iBAAW,YAAY,WAAW;AAEhC,cAAM,WAAW,MAAM,GAAG,QAAQ,cAAc;AAAA,UAC9C,QAAQ,SAAS;AAAA,UACjB;AAAA,UACA;AAAA,QACF,CAAC;AAED,YAAI,UAAU;AACZ,kBAAQ,IAAI,wBAAmB,SAAS,MAAM,kBAAkB;AAChE;AACA;AAAA,QACF;AAGA,cAAM,OAAO,GAAG,OAAO,cAAc;AAAA,UACnC,GAAG;AAAA,UACH;AAAA,UACA;AAAA,QACF,CAAC;AAED,cAAM,GAAG,QAAQ,IAAI,EAAE,MAAM;AAC7B,cAAM,qCAAqC,OAAO,UAAU,cAAc;AAC1E,gBAAQ,IAAI,+BAA0B,KAAK,QAAQ,EAAE;AACrD;AAAA,MACF;AAEA,cAAQ,IAAI;AAAA,qCAAmC;AAC/C,cAAQ,IAAI,eAAe,WAAW,EAAE;AACxC,cAAQ,IAAI,2BAA2B,YAAY,EAAE;AACrD,cAAQ,IAAI,cAAc,UAAU,MAAM,EAAE;AAAA,IAC9C,SAAS,OAAO;AACd,cAAQ,MAAM,8BAA8B,KAAK;AACjD,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,MAAM,2BAA2B;AAAA,EAC/B;AACF;AAEA,IAAO,cAAQ;",
6
6
  "names": []
7
7
  }