@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,15 @@ let _entityLinkSpecsCache = null;
4
4
  function getEntityLinkSpecs() {
5
5
  if (_entityLinkSpecsCache) return _entityLinkSpecsCache;
6
6
  const E = getEntityIds();
7
- _entityLinkSpecsCache = {
8
- [E.catalog.catalog_product]: {
7
+ const specs = {};
8
+ if (E.catalog?.catalog_product) {
9
+ specs[E.catalog.catalog_product] = {
9
10
  labelFields: ["title", "sku", "handle"],
10
11
  buildHref: (record) => buildSimpleHref("/backend/catalog/products", record.id)
11
- },
12
- [E.catalog.catalog_product_variant]: {
12
+ };
13
+ }
14
+ if (E.catalog?.catalog_product_variant) {
15
+ specs[E.catalog.catalog_product_variant] = {
13
16
  labelFields: ["name", "sku"],
14
17
  extraFields: ["product_id"],
15
18
  buildHref: (record) => {
@@ -17,8 +20,10 @@ function getEntityLinkSpecs() {
17
20
  if (!productId) return null;
18
21
  return `/backend/catalog/products/${encodeURIComponent(productId)}/variants/${encodeURIComponent(String(record.id ?? ""))}`;
19
22
  }
20
- },
21
- [E.customers.customer_entity]: {
23
+ };
24
+ }
25
+ if (E.customers?.customer_entity) {
26
+ specs[E.customers.customer_entity] = {
22
27
  labelFields: ["display_name"],
23
28
  extraFields: ["kind"],
24
29
  buildHref: (record) => {
@@ -27,32 +32,41 @@ function getEntityLinkSpecs() {
27
32
  if (kind === "person") return buildSimpleHref("/backend/customers/people", record.id);
28
33
  return null;
29
34
  }
30
- },
31
- [E.customers.customer_person_profile]: {
35
+ };
36
+ }
37
+ if (E.customers?.customer_person_profile) {
38
+ specs[E.customers.customer_person_profile] = {
32
39
  labelFields: ["preferred_name", "display_name", "first_name", "last_name"],
33
40
  extraFields: ["entity_id", "first_name", "last_name"],
34
41
  buildHref: (record) => {
35
42
  const entityId = readRecordValue(record, "entity_id");
36
43
  return entityId ? buildSimpleHref("/backend/customers/people", entityId) : null;
37
44
  }
38
- },
39
- [E.customers.customer_company_profile]: {
45
+ };
46
+ }
47
+ if (E.customers?.customer_company_profile) {
48
+ specs[E.customers.customer_company_profile] = {
40
49
  labelFields: ["brand_name", "display_name", "legal_name"],
41
50
  extraFields: ["entity_id"],
42
51
  buildHref: (record) => {
43
52
  const entityId = readRecordValue(record, "entity_id");
44
53
  return entityId ? buildSimpleHref("/backend/customers/companies", entityId) : null;
45
54
  }
46
- },
47
- [E.customers.customer_deal]: {
55
+ };
56
+ }
57
+ if (E.customers?.customer_deal) {
58
+ specs[E.customers.customer_deal] = {
48
59
  labelFields: ["title"],
49
60
  buildHref: (record) => buildSimpleHref("/backend/customers/deals", record.id)
50
- },
51
- [E.sales.sales_channel]: {
61
+ };
62
+ }
63
+ if (E.sales?.sales_channel) {
64
+ specs[E.sales.sales_channel] = {
52
65
  labelFields: ["name", "title"],
53
66
  buildHref: (record) => buildSimpleHref("/backend/sales/channels", record.id, "/edit")
54
- }
55
- };
67
+ };
68
+ }
69
+ _entityLinkSpecsCache = specs;
56
70
  return _entityLinkSpecsCache;
57
71
  }
58
72
  const LIBRARY_ENTITY_ID = "attachments:library";
@@ -128,7 +142,7 @@ function isUuid(value) {
128
142
  }
129
143
  function filterIdsForEntity(entityId, ids) {
130
144
  const E = getEntityIds();
131
- if (entityId === E.catalog.catalog_product_variant || entityId === E.catalog.catalog_product) {
145
+ if (entityId === E.catalog?.catalog_product_variant || entityId === E.catalog?.catalog_product) {
132
146
  return ids.filter((id) => isUuid(id));
133
147
  }
134
148
  return ids;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/attachments/lib/assignmentDetails.ts"],
4
- "sourcesContent": ["import type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type { AttachmentAssignment } from './metadata'\nimport type { CustomEntitySpec } from '@open-mercato/shared/modules/entities'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\n\ntype AssignmentLinkSpec = {\n labelFields?: string[]\n extraFields?: string[]\n buildHref?: (record: Record<string, unknown>) => string | null | undefined\n}\n\nlet _entityLinkSpecsCache: Record<string, AssignmentLinkSpec> | null = null\n\nfunction getEntityLinkSpecs(): Record<string, AssignmentLinkSpec> {\n if (_entityLinkSpecsCache) return _entityLinkSpecsCache\n const E = getEntityIds() as any\n _entityLinkSpecsCache = {\n [E.catalog.catalog_product]: {\n labelFields: ['title', 'sku', 'handle'],\n buildHref: (record) => buildSimpleHref('/backend/catalog/products', record.id),\n },\n [E.catalog.catalog_product_variant]: {\n labelFields: ['name', 'sku'],\n extraFields: ['product_id'],\n buildHref: (record) => {\n const productId = readRecordValue(record, 'product_id')\n if (!productId) return null\n return `/backend/catalog/products/${encodeURIComponent(productId)}/variants/${encodeURIComponent(String(record.id ?? ''))}`\n },\n },\n [E.customers.customer_entity]: {\n labelFields: ['display_name'],\n extraFields: ['kind'],\n buildHref: (record) => {\n const kind = String(readRecordValue(record, 'kind') || '').toLowerCase()\n if (kind === 'company') return buildSimpleHref('/backend/customers/companies', record.id)\n if (kind === 'person') return buildSimpleHref('/backend/customers/people', record.id)\n return null\n },\n },\n [E.customers.customer_person_profile]: {\n labelFields: ['preferred_name', 'display_name', 'first_name', 'last_name'],\n extraFields: ['entity_id', 'first_name', 'last_name'],\n buildHref: (record) => {\n const entityId = readRecordValue(record, 'entity_id')\n return entityId ? buildSimpleHref('/backend/customers/people', entityId) : null\n },\n },\n [E.customers.customer_company_profile]: {\n labelFields: ['brand_name', 'display_name', 'legal_name'],\n extraFields: ['entity_id'],\n buildHref: (record) => {\n const entityId = readRecordValue(record, 'entity_id')\n return entityId ? buildSimpleHref('/backend/customers/companies', entityId) : null\n },\n },\n [E.customers.customer_deal]: {\n labelFields: ['title'],\n buildHref: (record) => buildSimpleHref('/backend/customers/deals', record.id),\n },\n [E.sales.sales_channel]: {\n labelFields: ['name', 'title'],\n buildHref: (record) => buildSimpleHref('/backend/sales/channels', record.id, '/edit'),\n },\n }\n return _entityLinkSpecsCache\n}\n\nconst LIBRARY_ENTITY_ID = 'attachments:library'\nconst DEFAULT_LABEL_FIELDS = [\n 'label',\n 'title',\n 'name',\n 'display_name',\n 'displayName',\n 'subject',\n 'sku',\n 'handle',\n 'order_number',\n 'quote_number',\n 'invoice_number',\n 'email',\n 'company_name',\n 'legal_name',\n 'brand_name',\n]\n\nlet entitySpecsPromise: Promise<Map<string, CustomEntitySpec>> | null = null\n\nasync function loadEntitySpecs(): Promise<Map<string, CustomEntitySpec>> {\n if (!entitySpecsPromise) {\n entitySpecsPromise = Promise.resolve()\n .then(() => {\n const map = new Map<string, CustomEntitySpec>()\n const mods = getModules()\n for (const mod of mods) {\n const specs = ((mod as any).customEntities as CustomEntitySpec[] | undefined) ?? []\n for (const spec of specs) {\n if (spec?.id && !map.has(spec.id)) {\n map.set(spec.id, spec)\n }\n }\n }\n return map\n })\n .catch(() => new Map<string, CustomEntitySpec>())\n }\n return entitySpecsPromise\n}\n\nexport type AssignmentEnrichment = {\n label?: string\n href?: string\n}\n\nexport type AssignmentEnrichmentMap = Map<string, AssignmentEnrichment>\n\nfunction camelToSnake(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .toLowerCase()\n}\n\nfunction snakeToCamel(value: string): string {\n return value.replace(/[_-](\\w)/g, (_, c: string) => c.toUpperCase())\n}\n\nfunction normalizeValue(value: unknown): string | null {\n if (value === undefined || value === null) return null\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length ? trimmed : null\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n return null\n}\n\nfunction readRecordValue(record: Record<string, unknown>, field: string): string | null {\n if (!field) return null\n if (record[field] !== undefined) return normalizeValue(record[field])\n const snake = camelToSnake(field)\n if (snake !== field && record[snake] !== undefined) return normalizeValue(record[snake])\n const camel = snakeToCamel(field)\n if (camel !== field && record[camel] !== undefined) return normalizeValue(record[camel])\n return null\n}\n\nfunction buildSimpleHref(base: string, idValue: unknown, suffix: string = ''): string | null {\n const id = normalizeValue(idValue)\n if (!id) return null\n return `${base}/${encodeURIComponent(id)}${suffix}`\n}\n\nfunction isUuid(value: string | null | undefined): boolean {\n return typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value.trim())\n}\n\nfunction filterIdsForEntity(entityId: string, ids: string[]): string[] {\n const E = getEntityIds() as any\n if (entityId === E.catalog.catalog_product_variant || entityId === E.catalog.catalog_product) {\n return ids.filter((id) => isUuid(id))\n }\n return ids\n}\n\nfunction resolveLabelCandidates(entityId: string, linkSpec: AssignmentLinkSpec | undefined, entitySpecs: Map<string, CustomEntitySpec>): string[] {\n const candidates = new Set<string>()\n const spec = entitySpecs.get(entityId)\n if (spec?.labelField) {\n candidates.add(spec.labelField)\n candidates.add(camelToSnake(spec.labelField))\n }\n for (const field of linkSpec?.labelFields ?? []) {\n candidates.add(field)\n candidates.add(camelToSnake(field))\n }\n DEFAULT_LABEL_FIELDS.forEach((field) => {\n candidates.add(field)\n candidates.add(camelToSnake(field))\n })\n return Array.from(candidates).filter((field) => field.length > 0)\n}\n\nfunction buildLabel(\n record: Record<string, unknown>,\n entityId: string,\n linkSpec: AssignmentLinkSpec | undefined,\n entitySpecs: Map<string, CustomEntitySpec>\n): string | null {\n const candidates = resolveLabelCandidates(entityId, linkSpec, entitySpecs)\n for (const candidate of candidates) {\n const value = readRecordValue(record, candidate)\n if (value) return value\n }\n const fullName = [readRecordValue(record, 'first_name'), readRecordValue(record, 'last_name')]\n .filter((part): part is string => Boolean(part))\n .join(' ')\n .trim()\n if (fullName.length) return fullName\n return null\n}\n\nexport async function resolveAssignmentEnrichments(\n assignments: AttachmentAssignment[],\n opts: { queryEngine?: QueryEngine | null; tenantId: string; organizationId: string },\n): Promise<AssignmentEnrichmentMap> {\n const map: AssignmentEnrichmentMap = new Map()\n if (!assignments.length || !opts.queryEngine) return map\n const entitySpecs = await loadEntitySpecs()\n const grouped = new Map<string, Set<string>>()\n for (const assignment of assignments) {\n if (!assignment || assignment.type === LIBRARY_ENTITY_ID) continue\n const type = assignment.type?.trim()\n const id = assignment.id?.trim()\n if (!type || !id) continue\n if (!grouped.has(type)) grouped.set(type, new Set())\n grouped.get(type)!.add(id)\n }\n if (!grouped.size) return map\n\n const entityLinkSpecs = getEntityLinkSpecs()\n for (const [entityId, idsSet] of grouped.entries()) {\n const ids = filterIdsForEntity(entityId, Array.from(idsSet.values()))\n if (!ids.length) continue\n const linkSpec = entityLinkSpecs[entityId]\n const fields = new Set<string>(['id'])\n const candidates = resolveLabelCandidates(entityId, linkSpec, entitySpecs)\n candidates.forEach((field) => fields.add(field))\n for (const extra of linkSpec?.extraFields ?? []) {\n fields.add(extra)\n fields.add(camelToSnake(extra))\n }\n try {\n const result = await opts.queryEngine.query(entityId as any, {\n fields: Array.from(fields),\n filters: { id: ids.length === 1 ? { $eq: ids[0] } : { $in: ids } },\n tenantId: opts.tenantId,\n organizationId: opts.organizationId,\n page: { pageSize: Math.max(ids.length, 20) },\n })\n for (const record of result.items ?? []) {\n const recordId = normalizeValue((record as Record<string, unknown>).id)\n if (!recordId) continue\n const label = buildLabel(record as Record<string, unknown>, entityId, linkSpec, entitySpecs)\n const href = linkSpec?.buildHref?.(record as Record<string, unknown>) ?? null\n if (label || href) {\n map.set(`${entityId}:${recordId}`, {\n label: label ?? undefined,\n href: href ?? undefined,\n })\n }\n }\n } catch (error) {\n console.warn('[attachments] Failed to resolve assignment details for', entityId, error)\n }\n }\n return map\n}\n\nexport function applyAssignmentEnrichments(\n assignments: AttachmentAssignment[],\n enrichments: AssignmentEnrichmentMap,\n): AttachmentAssignment[] {\n if (!enrichments.size) return assignments\n return assignments.map((assignment) => {\n if (!assignment || !assignment.type || !assignment.id) return assignment\n const key = `${assignment.type}:${assignment.id}`\n const detail = enrichments.get(key)\n if (!detail) return assignment\n const next: AttachmentAssignment = { ...assignment }\n if (!next.label && detail.label) next.label = detail.label\n if (!next.href && detail.href) next.href = detail.href\n return next\n })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport type { AttachmentAssignment } from './metadata'\nimport type { CustomEntitySpec } from '@open-mercato/shared/modules/entities'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\n\ntype AssignmentLinkSpec = {\n labelFields?: string[]\n extraFields?: string[]\n buildHref?: (record: Record<string, unknown>) => string | null | undefined\n}\n\nlet _entityLinkSpecsCache: Record<string, AssignmentLinkSpec> | null = null\n\nfunction getEntityLinkSpecs(): Record<string, AssignmentLinkSpec> {\n if (_entityLinkSpecsCache) return _entityLinkSpecsCache\n const E = getEntityIds() as any\n const specs: Record<string, AssignmentLinkSpec> = {}\n\n if (E.catalog?.catalog_product) {\n specs[E.catalog.catalog_product] = {\n labelFields: ['title', 'sku', 'handle'],\n buildHref: (record) => buildSimpleHref('/backend/catalog/products', record.id),\n }\n }\n if (E.catalog?.catalog_product_variant) {\n specs[E.catalog.catalog_product_variant] = {\n labelFields: ['name', 'sku'],\n extraFields: ['product_id'],\n buildHref: (record) => {\n const productId = readRecordValue(record, 'product_id')\n if (!productId) return null\n return `/backend/catalog/products/${encodeURIComponent(productId)}/variants/${encodeURIComponent(String(record.id ?? ''))}`\n },\n }\n }\n if (E.customers?.customer_entity) {\n specs[E.customers.customer_entity] = {\n labelFields: ['display_name'],\n extraFields: ['kind'],\n buildHref: (record) => {\n const kind = String(readRecordValue(record, 'kind') || '').toLowerCase()\n if (kind === 'company') return buildSimpleHref('/backend/customers/companies', record.id)\n if (kind === 'person') return buildSimpleHref('/backend/customers/people', record.id)\n return null\n },\n }\n }\n if (E.customers?.customer_person_profile) {\n specs[E.customers.customer_person_profile] = {\n labelFields: ['preferred_name', 'display_name', 'first_name', 'last_name'],\n extraFields: ['entity_id', 'first_name', 'last_name'],\n buildHref: (record) => {\n const entityId = readRecordValue(record, 'entity_id')\n return entityId ? buildSimpleHref('/backend/customers/people', entityId) : null\n },\n }\n }\n if (E.customers?.customer_company_profile) {\n specs[E.customers.customer_company_profile] = {\n labelFields: ['brand_name', 'display_name', 'legal_name'],\n extraFields: ['entity_id'],\n buildHref: (record) => {\n const entityId = readRecordValue(record, 'entity_id')\n return entityId ? buildSimpleHref('/backend/customers/companies', entityId) : null\n },\n }\n }\n if (E.customers?.customer_deal) {\n specs[E.customers.customer_deal] = {\n labelFields: ['title'],\n buildHref: (record) => buildSimpleHref('/backend/customers/deals', record.id),\n }\n }\n if (E.sales?.sales_channel) {\n specs[E.sales.sales_channel] = {\n labelFields: ['name', 'title'],\n buildHref: (record) => buildSimpleHref('/backend/sales/channels', record.id, '/edit'),\n }\n }\n\n _entityLinkSpecsCache = specs\n return _entityLinkSpecsCache\n}\n\nconst LIBRARY_ENTITY_ID = 'attachments:library'\nconst DEFAULT_LABEL_FIELDS = [\n 'label',\n 'title',\n 'name',\n 'display_name',\n 'displayName',\n 'subject',\n 'sku',\n 'handle',\n 'order_number',\n 'quote_number',\n 'invoice_number',\n 'email',\n 'company_name',\n 'legal_name',\n 'brand_name',\n]\n\nlet entitySpecsPromise: Promise<Map<string, CustomEntitySpec>> | null = null\n\nasync function loadEntitySpecs(): Promise<Map<string, CustomEntitySpec>> {\n if (!entitySpecsPromise) {\n entitySpecsPromise = Promise.resolve()\n .then(() => {\n const map = new Map<string, CustomEntitySpec>()\n const mods = getModules()\n for (const mod of mods) {\n const specs = ((mod as any).customEntities as CustomEntitySpec[] | undefined) ?? []\n for (const spec of specs) {\n if (spec?.id && !map.has(spec.id)) {\n map.set(spec.id, spec)\n }\n }\n }\n return map\n })\n .catch(() => new Map<string, CustomEntitySpec>())\n }\n return entitySpecsPromise\n}\n\nexport type AssignmentEnrichment = {\n label?: string\n href?: string\n}\n\nexport type AssignmentEnrichmentMap = Map<string, AssignmentEnrichment>\n\nfunction camelToSnake(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[\\s-]+/g, '_')\n .toLowerCase()\n}\n\nfunction snakeToCamel(value: string): string {\n return value.replace(/[_-](\\w)/g, (_, c: string) => c.toUpperCase())\n}\n\nfunction normalizeValue(value: unknown): string | null {\n if (value === undefined || value === null) return null\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length ? trimmed : null\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n return null\n}\n\nfunction readRecordValue(record: Record<string, unknown>, field: string): string | null {\n if (!field) return null\n if (record[field] !== undefined) return normalizeValue(record[field])\n const snake = camelToSnake(field)\n if (snake !== field && record[snake] !== undefined) return normalizeValue(record[snake])\n const camel = snakeToCamel(field)\n if (camel !== field && record[camel] !== undefined) return normalizeValue(record[camel])\n return null\n}\n\nfunction buildSimpleHref(base: string, idValue: unknown, suffix: string = ''): string | null {\n const id = normalizeValue(idValue)\n if (!id) return null\n return `${base}/${encodeURIComponent(id)}${suffix}`\n}\n\nfunction isUuid(value: string | null | undefined): boolean {\n return typeof value === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value.trim())\n}\n\nfunction filterIdsForEntity(entityId: string, ids: string[]): string[] {\n const E = getEntityIds() as any\n if (entityId === E.catalog?.catalog_product_variant || entityId === E.catalog?.catalog_product) {\n return ids.filter((id) => isUuid(id))\n }\n return ids\n}\n\nfunction resolveLabelCandidates(entityId: string, linkSpec: AssignmentLinkSpec | undefined, entitySpecs: Map<string, CustomEntitySpec>): string[] {\n const candidates = new Set<string>()\n const spec = entitySpecs.get(entityId)\n if (spec?.labelField) {\n candidates.add(spec.labelField)\n candidates.add(camelToSnake(spec.labelField))\n }\n for (const field of linkSpec?.labelFields ?? []) {\n candidates.add(field)\n candidates.add(camelToSnake(field))\n }\n DEFAULT_LABEL_FIELDS.forEach((field) => {\n candidates.add(field)\n candidates.add(camelToSnake(field))\n })\n return Array.from(candidates).filter((field) => field.length > 0)\n}\n\nfunction buildLabel(\n record: Record<string, unknown>,\n entityId: string,\n linkSpec: AssignmentLinkSpec | undefined,\n entitySpecs: Map<string, CustomEntitySpec>\n): string | null {\n const candidates = resolveLabelCandidates(entityId, linkSpec, entitySpecs)\n for (const candidate of candidates) {\n const value = readRecordValue(record, candidate)\n if (value) return value\n }\n const fullName = [readRecordValue(record, 'first_name'), readRecordValue(record, 'last_name')]\n .filter((part): part is string => Boolean(part))\n .join(' ')\n .trim()\n if (fullName.length) return fullName\n return null\n}\n\nexport async function resolveAssignmentEnrichments(\n assignments: AttachmentAssignment[],\n opts: { queryEngine?: QueryEngine | null; tenantId: string; organizationId: string },\n): Promise<AssignmentEnrichmentMap> {\n const map: AssignmentEnrichmentMap = new Map()\n if (!assignments.length || !opts.queryEngine) return map\n const entitySpecs = await loadEntitySpecs()\n const grouped = new Map<string, Set<string>>()\n for (const assignment of assignments) {\n if (!assignment || assignment.type === LIBRARY_ENTITY_ID) continue\n const type = assignment.type?.trim()\n const id = assignment.id?.trim()\n if (!type || !id) continue\n if (!grouped.has(type)) grouped.set(type, new Set())\n grouped.get(type)!.add(id)\n }\n if (!grouped.size) return map\n\n const entityLinkSpecs = getEntityLinkSpecs()\n for (const [entityId, idsSet] of grouped.entries()) {\n const ids = filterIdsForEntity(entityId, Array.from(idsSet.values()))\n if (!ids.length) continue\n const linkSpec = entityLinkSpecs[entityId]\n const fields = new Set<string>(['id'])\n const candidates = resolveLabelCandidates(entityId, linkSpec, entitySpecs)\n candidates.forEach((field) => fields.add(field))\n for (const extra of linkSpec?.extraFields ?? []) {\n fields.add(extra)\n fields.add(camelToSnake(extra))\n }\n try {\n const result = await opts.queryEngine.query(entityId as any, {\n fields: Array.from(fields),\n filters: { id: ids.length === 1 ? { $eq: ids[0] } : { $in: ids } },\n tenantId: opts.tenantId,\n organizationId: opts.organizationId,\n page: { pageSize: Math.max(ids.length, 20) },\n })\n for (const record of result.items ?? []) {\n const recordId = normalizeValue((record as Record<string, unknown>).id)\n if (!recordId) continue\n const label = buildLabel(record as Record<string, unknown>, entityId, linkSpec, entitySpecs)\n const href = linkSpec?.buildHref?.(record as Record<string, unknown>) ?? null\n if (label || href) {\n map.set(`${entityId}:${recordId}`, {\n label: label ?? undefined,\n href: href ?? undefined,\n })\n }\n }\n } catch (error) {\n console.warn('[attachments] Failed to resolve assignment details for', entityId, error)\n }\n }\n return map\n}\n\nexport function applyAssignmentEnrichments(\n assignments: AttachmentAssignment[],\n enrichments: AssignmentEnrichmentMap,\n): AttachmentAssignment[] {\n if (!enrichments.size) return assignments\n return assignments.map((assignment) => {\n if (!assignment || !assignment.type || !assignment.id) return assignment\n const key = `${assignment.type}:${assignment.id}`\n const detail = enrichments.get(key)\n if (!detail) return assignment\n const next: AttachmentAssignment = { ...assignment }\n if (!next.label && detail.label) next.label = detail.label\n if (!next.href && detail.href) next.href = detail.href\n return next\n })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -16,9 +16,9 @@ const DEFAULT_ATTACHMENT_PARTITIONS = [
16
16
  isPublic: false
17
17
  }
18
18
  ];
19
- const PRODUCT_MEDIA_ENTITY_IDS = /* @__PURE__ */ new Set([
20
- E.catalog.catalog_product
21
- ]);
19
+ const PRODUCT_MEDIA_ENTITY_IDS = new Set(
20
+ [E.catalog?.catalog_product].filter(Boolean)
21
+ );
22
22
  const FALLBACK_PARTITION = "privateAttachments";
23
23
  function resolveDefaultPartitionCode(entityId) {
24
24
  if (!entityId) return FALLBACK_PARTITION;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/attachments/lib/partitions.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { AttachmentPartition } from '../data/entities'\nimport { resolveDefaultAttachmentOcrEnabled } from './ocrConfig'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { E } from '#generated/entities.ids.generated'\n\nexport type AttachmentPartitionSeed = {\n code: string\n title: string\n description?: string | null\n isPublic?: boolean\n}\n\nexport const DEFAULT_ATTACHMENT_PARTITIONS: AttachmentPartitionSeed[] = [\n {\n code: 'productsMedia',\n title: 'Products media',\n description: 'Public media uploaded for catalog products.',\n isPublic: true,\n },\n {\n code: 'privateAttachments',\n title: 'Private attachments',\n description: 'Internal attachments scoped to tenants and organizations.',\n isPublic: false,\n },\n]\n\nconst PRODUCT_MEDIA_ENTITY_IDS = new Set<string>([\n E.catalog.catalog_product,\n])\n\nconst FALLBACK_PARTITION = 'privateAttachments'\n\nexport function resolveDefaultPartitionCode(entityId: string | null | undefined): string {\n if (!entityId) return FALLBACK_PARTITION\n if (PRODUCT_MEDIA_ENTITY_IDS.has(entityId)) return 'productsMedia'\n return FALLBACK_PARTITION\n}\n\nexport async function ensureDefaultPartitions(em: EntityManager): Promise<void> {\n const repo = em.getRepository(AttachmentPartition)\n const existing = await repo.findAll({ fields: ['code'] })\n const existingCodes = new Set(existing.map((entry) => entry.code))\n const pending = DEFAULT_ATTACHMENT_PARTITIONS.filter((seed) => !existingCodes.has(seed.code))\n if (!pending.length) return\n for (const seed of pending) {\n const record = repo.create({\n code: seed.code,\n title: seed.title,\n description: seed.description ?? null,\n storageDriver: 'local',\n isPublic: seed.isPublic ?? false,\n requiresOcr: resolveDefaultAttachmentOcrEnabled(),\n })\n em.persist(record)\n }\n await em.flush()\n}\n\nexport function sanitizePartitionCode(input: string): string {\n const trimmed = input.trim()\n const normalized = trimmed.replace(/[^a-zA-Z0-9_-]/g, '')\n return normalized\n}\n\nexport function isPartitionSettingsLocked(): boolean {\n const demoModeParsed = parseBooleanToken(process.env.DEMO_MODE ?? '')\n const demoModeEnabled = demoModeParsed === false ? false : true\n const onboardingEnabled = parseBooleanToken(process.env.SELF_SERVICE_ONBOARDING_ENABLED ?? '') === true\n return demoModeEnabled || onboardingEnabled\n}\n"],
5
- "mappings": "AACA,SAAS,2BAA2B;AACpC,SAAS,0CAA0C;AACnD,SAAS,yBAAyB;AAClC,SAAS,SAAS;AASX,MAAM,gCAA2D;AAAA,EACtE;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AACF;AAEA,MAAM,2BAA2B,oBAAI,IAAY;AAAA,EAC/C,EAAE,QAAQ;AACZ,CAAC;AAED,MAAM,qBAAqB;AAEpB,SAAS,4BAA4B,UAA6C;AACvF,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,yBAAyB,IAAI,QAAQ,EAAG,QAAO;AACnD,SAAO;AACT;AAEA,eAAsB,wBAAwB,IAAkC;AAC9E,QAAM,OAAO,GAAG,cAAc,mBAAmB;AACjD,QAAM,WAAW,MAAM,KAAK,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;AACxD,QAAM,gBAAgB,IAAI,IAAI,SAAS,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC;AACjE,QAAM,UAAU,8BAA8B,OAAO,CAAC,SAAS,CAAC,cAAc,IAAI,KAAK,IAAI,CAAC;AAC5F,MAAI,CAAC,QAAQ,OAAQ;AACrB,aAAW,QAAQ,SAAS;AAC1B,UAAM,SAAS,KAAK,OAAO;AAAA,MACzB,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK,eAAe;AAAA,MACjC,eAAe;AAAA,MACf,UAAU,KAAK,YAAY;AAAA,MAC3B,aAAa,mCAAmC;AAAA,IAClD,CAAC;AACD,OAAG,QAAQ,MAAM;AAAA,EACnB;AACA,QAAM,GAAG,MAAM;AACjB;AAEO,SAAS,sBAAsB,OAAuB;AAC3D,QAAM,UAAU,MAAM,KAAK;AAC3B,QAAM,aAAa,QAAQ,QAAQ,mBAAmB,EAAE;AACxD,SAAO;AACT;AAEO,SAAS,4BAAqC;AACnD,QAAM,iBAAiB,kBAAkB,QAAQ,IAAI,aAAa,EAAE;AACpE,QAAM,kBAAkB,mBAAmB,QAAQ,QAAQ;AAC3D,QAAM,oBAAoB,kBAAkB,QAAQ,IAAI,mCAAmC,EAAE,MAAM;AACnG,SAAO,mBAAmB;AAC5B;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { AttachmentPartition } from '../data/entities'\nimport { resolveDefaultAttachmentOcrEnabled } from './ocrConfig'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { E } from '#generated/entities.ids.generated'\n\nexport type AttachmentPartitionSeed = {\n code: string\n title: string\n description?: string | null\n isPublic?: boolean\n}\n\nexport const DEFAULT_ATTACHMENT_PARTITIONS: AttachmentPartitionSeed[] = [\n {\n code: 'productsMedia',\n title: 'Products media',\n description: 'Public media uploaded for catalog products.',\n isPublic: true,\n },\n {\n code: 'privateAttachments',\n title: 'Private attachments',\n description: 'Internal attachments scoped to tenants and organizations.',\n isPublic: false,\n },\n]\n\nconst PRODUCT_MEDIA_ENTITY_IDS = new Set<string>(\n [(E as any).catalog?.catalog_product].filter(Boolean) as string[]\n)\n\nconst FALLBACK_PARTITION = 'privateAttachments'\n\nexport function resolveDefaultPartitionCode(entityId: string | null | undefined): string {\n if (!entityId) return FALLBACK_PARTITION\n if (PRODUCT_MEDIA_ENTITY_IDS.has(entityId)) return 'productsMedia'\n return FALLBACK_PARTITION\n}\n\nexport async function ensureDefaultPartitions(em: EntityManager): Promise<void> {\n const repo = em.getRepository(AttachmentPartition)\n const existing = await repo.findAll({ fields: ['code'] })\n const existingCodes = new Set(existing.map((entry) => entry.code))\n const pending = DEFAULT_ATTACHMENT_PARTITIONS.filter((seed) => !existingCodes.has(seed.code))\n if (!pending.length) return\n for (const seed of pending) {\n const record = repo.create({\n code: seed.code,\n title: seed.title,\n description: seed.description ?? null,\n storageDriver: 'local',\n isPublic: seed.isPublic ?? false,\n requiresOcr: resolveDefaultAttachmentOcrEnabled(),\n })\n em.persist(record)\n }\n await em.flush()\n}\n\nexport function sanitizePartitionCode(input: string): string {\n const trimmed = input.trim()\n const normalized = trimmed.replace(/[^a-zA-Z0-9_-]/g, '')\n return normalized\n}\n\nexport function isPartitionSettingsLocked(): boolean {\n const demoModeParsed = parseBooleanToken(process.env.DEMO_MODE ?? '')\n const demoModeEnabled = demoModeParsed === false ? false : true\n const onboardingEnabled = parseBooleanToken(process.env.SELF_SERVICE_ONBOARDING_ENABLED ?? '') === true\n return demoModeEnabled || onboardingEnabled\n}\n"],
5
+ "mappings": "AACA,SAAS,2BAA2B;AACpC,SAAS,0CAA0C;AACnD,SAAS,yBAAyB;AAClC,SAAS,SAAS;AASX,MAAM,gCAA2D;AAAA,EACtE;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AACF;AAEA,MAAM,2BAA2B,IAAI;AAAA,EACnC,CAAE,EAAU,SAAS,eAAe,EAAE,OAAO,OAAO;AACtD;AAEA,MAAM,qBAAqB;AAEpB,SAAS,4BAA4B,UAA6C;AACvF,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,yBAAyB,IAAI,QAAQ,EAAG,QAAO;AACnD,SAAO;AACT;AAEA,eAAsB,wBAAwB,IAAkC;AAC9E,QAAM,OAAO,GAAG,cAAc,mBAAmB;AACjD,QAAM,WAAW,MAAM,KAAK,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;AACxD,QAAM,gBAAgB,IAAI,IAAI,SAAS,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC;AACjE,QAAM,UAAU,8BAA8B,OAAO,CAAC,SAAS,CAAC,cAAc,IAAI,KAAK,IAAI,CAAC;AAC5F,MAAI,CAAC,QAAQ,OAAQ;AACrB,aAAW,QAAQ,SAAS;AAC1B,UAAM,SAAS,KAAK,OAAO;AAAA,MACzB,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK,eAAe;AAAA,MACjC,eAAe;AAAA,MACf,UAAU,KAAK,YAAY;AAAA,MAC3B,aAAa,mCAAmC;AAAA,IAClD,CAAC;AACD,OAAG,QAAQ,MAAM;AAAA,EACnB;AACA,QAAM,GAAG,MAAM;AACjB;AAEO,SAAS,sBAAsB,OAAuB;AAC3D,QAAM,UAAU,MAAM,KAAK;AAC3B,QAAM,aAAa,QAAQ,QAAQ,mBAAmB,EAAE;AACxD,SAAO;AACT;AAEO,SAAS,4BAAqC;AACnD,QAAM,iBAAiB,kBAAkB,QAAQ,IAAI,aAAa,EAAE;AACpE,QAAM,kBAAkB,mBAAmB,QAAQ,QAAQ;AAC3D,QAAM,oBAAoB,kBAAkB,QAAQ,IAAI,mCAAmC,EAAE,MAAM;AACnG,SAAO,mBAAmB;AAC5B;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,11 @@
1
+ const setup = {
2
+ defaultRoleFeatures: {
3
+ admin: ["attachments.*", "attachments.view", "attachments.manage"]
4
+ }
5
+ };
6
+ var setup_default = setup;
7
+ export {
8
+ setup_default as default,
9
+ setup
10
+ };
11
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/attachments/setup.ts"],
4
+ "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['attachments.*', 'attachments.view', 'attachments.manage'],\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO,CAAC,iBAAiB,oBAAoB,oBAAoB;AAAA,EACnE;AACF;AAEA,IAAO,gBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ const setup = {
2
+ defaultRoleFeatures: {
3
+ admin: ["audit_logs.*"],
4
+ employee: ["audit_logs.undo_self"]
5
+ }
6
+ };
7
+ var setup_default = setup;
8
+ export {
9
+ setup_default as default,
10
+ setup
11
+ };
12
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/audit_logs/setup.ts"],
4
+ "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['audit_logs.*'],\n employee: ['audit_logs.undo_self'],\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO,CAAC,cAAc;AAAA,IACtB,UAAU,CAAC,sBAAsB;AAAA,EACnC;AACF;AAEA,IAAO,gBAAQ;",
6
+ "names": []
7
+ }
@@ -3,11 +3,6 @@ import { Role, RoleAcl, User, UserRole } from "@open-mercato/core/modules/auth/d
3
3
  import { Tenant, Organization } from "@open-mercato/core/modules/directory/data/entities";
4
4
  import { rebuildHierarchyForTenant } from "@open-mercato/core/modules/directory/lib/hierarchy";
5
5
  import { normalizeTenantId } from "./tenantAccess.js";
6
- import { SalesSettings, SalesDocumentSequence } from "@open-mercato/core/modules/sales/data/entities";
7
- import {
8
- DEFAULT_ORDER_NUMBER_FORMAT,
9
- DEFAULT_QUOTE_NUMBER_FORMAT
10
- } from "@open-mercato/core/modules/sales/lib/documentNumberTokens";
11
6
  import { computeEmailHash } from "@open-mercato/core/modules/auth/lib/emailHash";
12
7
  import { isEncryptionDebugEnabled, isTenantDataEncryptionEnabled } from "@open-mercato/shared/lib/encryption/toggles";
13
8
  import { EncryptionMap } from "@open-mercato/core/modules/entities/data/entities";
@@ -242,9 +237,14 @@ async function setupInitialTenant(em, options) {
242
237
  if (!reusedExistingUser) {
243
238
  await rebuildHierarchyForTenant(em, tenantId);
244
239
  }
245
- await ensureDefaultRoleAcls(em, tenantId, { includeSuperadminRole });
240
+ const resolvedModules = options.modules ?? tryGetModules();
241
+ await ensureDefaultRoleAcls(em, tenantId, resolvedModules, { includeSuperadminRole });
246
242
  await deactivateDemoSuperAdminIfSelfOnboardingEnabled(em);
247
- await ensureSalesNumberingDefaults(em, { tenantId, organizationId });
243
+ for (const mod of resolvedModules) {
244
+ if (mod.setup?.onTenantCreated) {
245
+ await mod.setup.onTenantCreated({ em, tenantId, organizationId });
246
+ }
247
+ }
248
248
  return {
249
249
  tenantId,
250
250
  organizationId,
@@ -263,90 +263,30 @@ async function resolvePasswordHash(input) {
263
263
  if (input.password) return hash(input.password, 10);
264
264
  return null;
265
265
  }
266
- async function ensureDefaultRoleAcls(em, tenantId, options = {}) {
266
+ async function ensureDefaultRoleAcls(em, tenantId, modules, options = {}) {
267
267
  const includeSuperadminRole = options.includeSuperadminRole ?? true;
268
268
  const roleTenantId = normalizeTenantId(tenantId) ?? null;
269
269
  const superadminRole = includeSuperadminRole ? await findRoleByName(em, "superadmin", roleTenantId) : null;
270
270
  const adminRole = await findRoleByName(em, "admin", roleTenantId);
271
271
  const employeeRole = await findRoleByName(em, "employee", roleTenantId);
272
+ const superadminFeatures = [];
273
+ const adminFeatures = [];
274
+ const employeeFeatures = [];
275
+ for (const mod of modules) {
276
+ const roleFeatures = mod.setup?.defaultRoleFeatures;
277
+ if (!roleFeatures) continue;
278
+ if (roleFeatures.superadmin) superadminFeatures.push(...roleFeatures.superadmin);
279
+ if (roleFeatures.admin) adminFeatures.push(...roleFeatures.admin);
280
+ if (roleFeatures.employee) employeeFeatures.push(...roleFeatures.employee);
281
+ }
272
282
  if (includeSuperadminRole && superadminRole) {
273
- await ensureRoleAclFor(em, superadminRole, tenantId, ["directory.tenants.*"], { isSuperAdmin: true });
283
+ await ensureRoleAclFor(em, superadminRole, tenantId, superadminFeatures, { isSuperAdmin: true });
274
284
  }
275
285
  if (adminRole) {
276
- const adminFeatures = [
277
- "auth.*",
278
- "entities.*",
279
- "attachments.*",
280
- "attachments.view",
281
- "attachments.manage",
282
- "query_index.*",
283
- "search.*",
284
- "vector.*",
285
- "feature_toggles.*",
286
- "configs.system_status.view",
287
- "configs.cache.view",
288
- "configs.cache.manage",
289
- "configs.manage",
290
- "catalog.*",
291
- "catalog.variants.manage",
292
- "catalog.pricing.manage",
293
- "sales.*",
294
- "audit_logs.*",
295
- "directory.organizations.view",
296
- "directory.organizations.manage",
297
- "customers.*",
298
- "customers.people.view",
299
- "customers.people.manage",
300
- "customers.companies.view",
301
- "customers.companies.manage",
302
- "customers.deals.view",
303
- "customers.deals.manage",
304
- "dictionaries.view",
305
- "dictionaries.manage",
306
- "example.*",
307
- "dashboards.*",
308
- "dashboards.admin.assign-widgets",
309
- "analytics.view",
310
- "api_keys.*",
311
- "perspectives.use",
312
- "perspectives.role_defaults",
313
- "business_rules.*",
314
- "workflows.*",
315
- "currencies.*",
316
- "staff.*",
317
- "staff.leave_requests.manage",
318
- "resources.*",
319
- "planner.*"
320
- ];
321
- await ensureRoleAclFor(em, adminRole, tenantId, adminFeatures, { remove: ["directory.organizations.*", "directory.tenants.*"] });
286
+ await ensureRoleAclFor(em, adminRole, tenantId, adminFeatures);
322
287
  }
323
288
  if (employeeRole) {
324
- await ensureRoleAclFor(em, employeeRole, tenantId, [
325
- "customers.*",
326
- "customers.people.view",
327
- "customers.people.manage",
328
- "customers.companies.view",
329
- "customers.companies.manage",
330
- "vector.*",
331
- "catalog.*",
332
- "catalog.variants.manage",
333
- "catalog.pricing.manage",
334
- "sales.*",
335
- "dictionaries.view",
336
- "example.*",
337
- "example.widgets.*",
338
- "dashboards.view",
339
- "dashboards.configure",
340
- "analytics.view",
341
- "audit_logs.undo_self",
342
- "perspectives.use",
343
- "staff.leave_requests.send",
344
- "staff.my_availability.view",
345
- "staff.my_availability.manage",
346
- "staff.my_leave_requests.view",
347
- "staff.my_leave_requests.send",
348
- "planner.view"
349
- ]);
289
+ await ensureRoleAclFor(em, employeeRole, tenantId, employeeFeatures);
350
290
  }
351
291
  }
352
292
  async function ensureRoleAclFor(em, role, tenantId, features, options = {}) {
@@ -364,19 +304,8 @@ async function ensureRoleAclFor(em, role, tenantId, features, options = {}) {
364
304
  }
365
305
  const currentFeatures = Array.isArray(existing.featuresJson) ? existing.featuresJson : [];
366
306
  const merged = Array.from(/* @__PURE__ */ new Set([...currentFeatures, ...features]));
367
- const removeSet = new Set(options.remove ?? []);
368
- const sanitized = removeSet.size ? merged.filter((value) => {
369
- if (removeSet.has(value)) return false;
370
- for (const entry of removeSet) {
371
- if (entry.endsWith(".*")) {
372
- const prefix = entry.slice(0, -1);
373
- if (value === entry || value.startsWith(prefix)) return false;
374
- }
375
- }
376
- return true;
377
- }) : merged;
378
- const changed = sanitized.length !== currentFeatures.length || sanitized.some((value, index) => value !== currentFeatures[index]);
379
- if (changed) existing.featuresJson = sanitized;
307
+ const changed = merged.length !== currentFeatures.length || merged.some((value, index) => value !== currentFeatures[index]);
308
+ if (changed) existing.featuresJson = merged;
380
309
  if (options.isSuperAdmin && !existing.isSuperAdmin) {
381
310
  existing.isSuperAdmin = true;
382
311
  }
@@ -405,71 +334,12 @@ async function deactivateDemoSuperAdminIfSelfOnboardingEnabled(em) {
405
334
  console.error("[auth.setup] failed to deactivate demo superadmin user", error);
406
335
  }
407
336
  }
408
- async function ensureSalesNumberingDefaults(em, scope) {
409
- const repo = em.getRepository?.(SalesSettings);
410
- const findSettings = async () => repo?.findOne({
411
- tenantId: scope.tenantId,
412
- organizationId: scope.organizationId
413
- }) ?? em.findOne?.(SalesSettings, {
414
- tenantId: scope.tenantId,
415
- organizationId: scope.organizationId
416
- });
417
- const exists = await findSettings();
418
- if (!exists) {
419
- const settings = repo?.create?.({
420
- tenantId: scope.tenantId,
421
- organizationId: scope.organizationId,
422
- orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,
423
- quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,
424
- createdAt: /* @__PURE__ */ new Date(),
425
- updatedAt: /* @__PURE__ */ new Date()
426
- }) ?? em.create?.(SalesSettings, {
427
- tenantId: scope.tenantId,
428
- organizationId: scope.organizationId,
429
- orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,
430
- quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,
431
- createdAt: /* @__PURE__ */ new Date(),
432
- updatedAt: /* @__PURE__ */ new Date()
433
- });
434
- if (settings && em.persist) {
435
- em.persist(settings);
436
- }
437
- }
438
- const sequenceRepo = em.getRepository?.(SalesDocumentSequence);
439
- const kinds = ["order", "quote"];
440
- for (const kind of kinds) {
441
- const seq = sequenceRepo?.findOne({
442
- tenantId: scope.tenantId,
443
- organizationId: scope.organizationId,
444
- documentKind: kind
445
- }) ?? em.findOne?.(SalesDocumentSequence, {
446
- tenantId: scope.tenantId,
447
- organizationId: scope.organizationId,
448
- documentKind: kind
449
- });
450
- if (!seq) {
451
- const entry = sequenceRepo?.create?.({
452
- tenantId: scope.tenantId,
453
- organizationId: scope.organizationId,
454
- documentKind: kind,
455
- currentValue: 0,
456
- createdAt: /* @__PURE__ */ new Date(),
457
- updatedAt: /* @__PURE__ */ new Date()
458
- }) ?? em.create?.(SalesDocumentSequence, {
459
- tenantId: scope.tenantId,
460
- organizationId: scope.organizationId,
461
- documentKind: kind,
462
- currentValue: 0,
463
- createdAt: /* @__PURE__ */ new Date(),
464
- updatedAt: /* @__PURE__ */ new Date()
465
- });
466
- if (entry && em.persist) {
467
- em.persist(entry);
468
- }
469
- }
470
- }
471
- if (em.flush) {
472
- await em.flush();
337
+ function tryGetModules() {
338
+ try {
339
+ const { getModules } = require("@open-mercato/shared/lib/modules/registry");
340
+ return getModules();
341
+ } catch {
342
+ return [];
473
343
  }
474
344
  }
475
345
  export {