@open-mercato/cli 0.6.7-develop.6862.1.c11a64ce0a → 0.6.7

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 (278) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/build.mjs +7 -24
  3. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +88 -32
  4. package/dist/agentic/codex/enforcement-rules.md +16 -2
  5. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +50 -19
  6. package/dist/agentic/cursor/rules/entity-guard.mdc +21 -9
  7. package/dist/agentic/cursor/rules/generated-guard.mdc +5 -3
  8. package/dist/agentic/cursor/rules/open-mercato.mdc +29 -3
  9. package/dist/agentic/guides/cache.md +45 -0
  10. package/dist/agentic/guides/core.md +453 -0
  11. package/dist/agentic/guides/events.md +117 -0
  12. package/dist/agentic/guides/module-facts.json +3833 -115419
  13. package/dist/agentic/guides/module-system.md +127 -12
  14. package/dist/agentic/guides/modules/ai_assistant.md +28 -227
  15. package/dist/agentic/guides/modules/api_docs.md +2 -58
  16. package/dist/agentic/guides/modules/api_keys.md +7 -92
  17. package/dist/agentic/guides/modules/attachments.md +14 -119
  18. package/dist/agentic/guides/modules/audit_logs.md +10 -131
  19. package/dist/agentic/guides/modules/auth.md +33 -276
  20. package/dist/agentic/guides/modules/business_rules.md +14 -149
  21. package/dist/agentic/guides/modules/catalog.md +43 -475
  22. package/dist/agentic/guides/modules/channel_gmail.md +3 -82
  23. package/dist/agentic/guides/modules/channel_imap.md +3 -82
  24. package/dist/agentic/guides/modules/checkout.md +27 -299
  25. package/dist/agentic/guides/modules/communication_channels.md +33 -315
  26. package/dist/agentic/guides/modules/configs.md +8 -104
  27. package/dist/agentic/guides/modules/content.md +2 -57
  28. package/dist/agentic/guides/modules/currencies.md +17 -152
  29. package/dist/agentic/guides/modules/customer_accounts.md +45 -387
  30. package/dist/agentic/guides/modules/customers.md +85 -746
  31. package/dist/agentic/guides/modules/dashboards.md +10 -155
  32. package/dist/agentic/guides/modules/data_sync.md +16 -149
  33. package/dist/agentic/guides/modules/dictionaries.md +12 -107
  34. package/dist/agentic/guides/modules/directory.md +15 -148
  35. package/dist/agentic/guides/modules/entities.md +13 -145
  36. package/dist/agentic/guides/modules/events.md +2 -59
  37. package/dist/agentic/guides/modules/feature_toggles.md +9 -119
  38. package/dist/agentic/guides/modules/gateway_stripe.md +4 -92
  39. package/dist/agentic/guides/modules/generators.md +2 -45
  40. package/dist/agentic/guides/modules/inbox_ops.md +27 -207
  41. package/dist/agentic/guides/modules/integrations.md +16 -146
  42. package/dist/agentic/guides/modules/messages.md +24 -223
  43. package/dist/agentic/guides/modules/notifications.md +8 -96
  44. package/dist/agentic/guides/modules/onboarding.md +6 -77
  45. package/dist/agentic/guides/modules/payment_gateways.md +21 -164
  46. package/dist/agentic/guides/modules/perspectives.md +7 -77
  47. package/dist/agentic/guides/modules/planner.md +18 -136
  48. package/dist/agentic/guides/modules/portal.md +2 -93
  49. package/dist/agentic/guides/modules/progress.md +14 -104
  50. package/dist/agentic/guides/modules/query_index.md +15 -154
  51. package/dist/agentic/guides/modules/record_locks.md +21 -206
  52. package/dist/agentic/guides/modules/resources.md +30 -197
  53. package/dist/agentic/guides/modules/sales.md +84 -678
  54. package/dist/agentic/guides/modules/scheduler.md +14 -133
  55. package/dist/agentic/guides/modules/search.md +6 -170
  56. package/dist/agentic/guides/modules/security.md +35 -294
  57. package/dist/agentic/guides/modules/shipping_carriers.md +18 -134
  58. package/dist/agentic/guides/modules/sso.md +26 -151
  59. package/dist/agentic/guides/modules/staff.md +57 -369
  60. package/dist/agentic/guides/modules/storage_s3.md +5 -95
  61. package/dist/agentic/guides/modules/sync_akeneo.md +3 -85
  62. package/dist/agentic/guides/modules/sync_excel.md +6 -75
  63. package/dist/agentic/guides/modules/system_status_overlays.md +2 -60
  64. package/dist/agentic/guides/modules/translations.md +10 -104
  65. package/dist/agentic/guides/modules/webhooks.md +24 -203
  66. package/dist/agentic/guides/modules/wms.md +46 -441
  67. package/dist/agentic/guides/modules/workflows.md +43 -302
  68. package/dist/agentic/guides/queue.md +53 -0
  69. package/dist/agentic/guides/search.md +115 -0
  70. package/dist/agentic/guides/shared.md +108 -0
  71. package/dist/agentic/guides/ui.md +347 -0
  72. package/dist/agentic/shared/AGENTS.md.template +311 -124
  73. package/dist/agentic/shared/ai/agentic.config.json +0 -2
  74. package/dist/agentic/shared/ai/lessons.md +3 -26
  75. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +27 -0
  76. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +27 -0
  77. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +0 -4
  78. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +241 -20
  79. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +260 -0
  80. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +601 -16
  81. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +146 -0
  82. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +310 -14
  83. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +97 -15
  84. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +84 -0
  85. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +193 -0
  86. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +152 -33
  87. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +708 -24
  88. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +34 -0
  89. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +762 -0
  90. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +820 -20
  91. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +50 -0
  92. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +97 -0
  93. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +843 -19
  94. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +271 -0
  95. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +63 -15
  96. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +467 -16
  97. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +101 -0
  98. package/dist/agentic/shared/ai/skills/tiers.json +21 -143
  99. package/dist/agentic/shared/ai/skills/tiers.schema.json +40 -52
  100. package/dist/agentic/shared/ai/specs/README.md +3 -4
  101. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +10 -226
  102. package/dist/agentic/shared/ai/trackers/github.md +4 -108
  103. package/dist/agentic/shared/scripts/install-skills.sh +694 -4
  104. package/dist/bin.js +1 -13
  105. package/dist/bin.js.map +2 -2
  106. package/dist/lib/__integration__/TC-INT-008.spec.js +8 -88
  107. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  108. package/dist/lib/agentic-init.js +3 -13
  109. package/dist/lib/agentic-init.js.map +2 -2
  110. package/dist/lib/agentic-setup.js +186 -418
  111. package/dist/lib/agentic-setup.js.map +2 -2
  112. package/dist/lib/generators/extensions/injection-widgets.js +0 -20
  113. package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
  114. package/dist/lib/generators/module-facts-discovery.js +7 -41
  115. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  116. package/dist/lib/generators/module-facts.js +45 -1387
  117. package/dist/lib/generators/module-facts.js.map +3 -3
  118. package/dist/lib/generators/module-registry.js +5 -11
  119. package/dist/lib/generators/module-registry.js.map +2 -2
  120. package/dist/lib/resolver.js +0 -2
  121. package/dist/lib/resolver.js.map +2 -2
  122. package/dist/lib/testing/integration.js +0 -5
  123. package/dist/lib/testing/integration.js.map +2 -2
  124. package/dist/mercato.js +1 -23
  125. package/dist/mercato.js.map +2 -2
  126. package/package.json +11 -13
  127. package/src/bin.ts +1 -33
  128. package/src/lib/__integration__/TC-INT-008.spec.ts +9 -107
  129. package/src/lib/__tests__/agentic-init.test.ts +5 -41
  130. package/src/lib/agentic-init.ts +3 -15
  131. package/src/lib/agentic-setup.ts +224 -530
  132. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +2 -88
  133. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +0 -1
  134. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +10 -54
  135. package/src/lib/generators/__tests__/module-subset.test.ts +0 -18
  136. package/src/lib/generators/extensions/injection-widgets.ts +0 -51
  137. package/src/lib/generators/module-facts-discovery.ts +7 -58
  138. package/src/lib/generators/module-facts.ts +46 -1851
  139. package/src/lib/generators/module-registry.ts +6 -16
  140. package/src/lib/resolver.ts +0 -3
  141. package/src/lib/testing/__tests__/integration.test.ts +0 -4
  142. package/src/lib/testing/integration.ts +0 -5
  143. package/src/mercato.ts +1 -26
  144. package/dist/agentic/guides/ai-workflows.md +0 -76
  145. package/dist/agentic/guides/architecture.md +0 -107
  146. package/dist/agentic/guides/backend-ui.md +0 -80
  147. package/dist/agentic/guides/contracts.md +0 -92
  148. package/dist/agentic/guides/extensions.md +0 -121
  149. package/dist/agentic/guides/framework-extension-points.md +0 -36
  150. package/dist/agentic/guides/integrations.md +0 -81
  151. package/dist/agentic/guides/modules/design_system.md +0 -102
  152. package/dist/agentic/guides/spec-delivery.md +0 -22
  153. package/dist/agentic/guides/testing-debugging.md +0 -80
  154. package/dist/agentic/guides/upstream/AGENTS.md +0 -298
  155. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +0 -316
  156. package/dist/agentic/guides/upstream/manifest.json +0 -8
  157. package/dist/agentic/shared/ai/harness/README.md +0 -50
  158. package/dist/agentic/shared/ai/harness/RELEASE.md +0 -57
  159. package/dist/agentic/shared/ai/harness/cases.json +0 -13680
  160. package/dist/agentic/shared/ai/harness/cases.schema.json +0 -133
  161. package/dist/agentic/shared/ai/harness/fixtures/index.json +0 -49
  162. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +0 -162
  163. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +0 -21
  164. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +0 -96
  165. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +0 -172
  166. package/dist/agentic/shared/ai/harness/release-matrix.json +0 -158
  167. package/dist/agentic/shared/ai/harness/release-result.schema.json +0 -219
  168. package/dist/agentic/shared/ai/harness/result.schema.json +0 -76
  169. package/dist/agentic/shared/ai/harness/routing-response.schema.json +0 -12
  170. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +0 -73
  171. package/dist/agentic/shared/ai/harness/validators.json +0 -78
  172. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +0 -2121
  173. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +0 -1096
  174. package/dist/agentic/shared/ai/lessons/_template.md +0 -16
  175. package/dist/agentic/shared/ai/review-checklist.md +0 -36
  176. package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +0 -21
  177. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +0 -98
  178. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +0 -16
  179. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +0 -53
  180. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +0 -13
  181. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +0 -11
  182. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +0 -30
  183. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +0 -12
  184. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +0 -13
  185. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +0 -14
  186. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +0 -28
  187. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +0 -12
  188. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +0 -22
  189. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +0 -12
  190. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +0 -19
  191. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +0 -19
  192. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +0 -13
  193. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +0 -14
  194. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +0 -10
  195. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +0 -17
  196. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +0 -29
  197. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +0 -100
  198. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +0 -16
  199. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +0 -17
  200. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +0 -25
  201. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +0 -11
  202. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +0 -13
  203. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +0 -18
  204. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +0 -20
  205. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +0 -33
  206. package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +0 -40
  207. package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +0 -27
  208. package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +0 -24
  209. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +0 -16
  210. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +0 -14
  211. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +0 -15
  212. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +0 -12
  213. package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +0 -34
  214. package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +0 -4
  215. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +0 -11
  216. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +0 -31
  217. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +0 -39
  218. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +0 -43
  219. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +0 -11
  220. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +0 -19
  221. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +0 -172
  222. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +0 -12
  223. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +0 -83
  224. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +0 -21
  225. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +0 -11
  226. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +0 -22
  227. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +0 -13
  228. package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +0 -41
  229. package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +0 -19
  230. package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +0 -44
  231. package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +0 -39
  232. package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +0 -20
  233. package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +0 -57
  234. package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +0 -11
  235. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +0 -486
  236. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +0 -18
  237. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +0 -27
  238. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +0 -13
  239. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +0 -41
  240. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +0 -11
  241. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +0 -15
  242. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +0 -16
  243. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +0 -14
  244. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +0 -187
  245. package/dist/agentic/shared/scripts/check-lessons.mjs +0 -178
  246. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +0 -2958
  247. package/dist/agentic/shared/scripts/execution-sandbox.mjs +0 -329
  248. package/dist/agentic/shared/scripts/framework-context.mjs +0 -888
  249. package/dist/agentic/shared/scripts/install-skills.mjs +0 -1029
  250. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +0 -222
  251. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +0 -1774
  252. package/dist/lib/generators/module-extension-facts.js +0 -1840
  253. package/dist/lib/generators/module-extension-facts.js.map +0 -7
  254. package/dist/lib/generators/module-fact-sources.js +0 -70
  255. package/dist/lib/generators/module-fact-sources.js.map +0 -7
  256. package/dist/lib/generators/module-override-targets.js +0 -688
  257. package/dist/lib/generators/module-override-targets.js.map +0 -7
  258. package/dist/lib/load-env.js +0 -24
  259. package/dist/lib/load-env.js.map +0 -7
  260. package/dist/lib/telemetry-init.js +0 -358
  261. package/dist/lib/telemetry-init.js.map +0 -7
  262. package/src/lib/__tests__/agentic-setup.ownership.test.ts +0 -386
  263. package/src/lib/__tests__/injection-widget-id-validation.test.ts +0 -184
  264. package/src/lib/__tests__/load-env.test.ts +0 -65
  265. package/src/lib/__tests__/telemetry-init.test.ts +0 -298
  266. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +0 -92
  267. package/src/lib/generators/__tests__/module-extension-activations.test.ts +0 -762
  268. package/src/lib/generators/__tests__/module-extension-facts.test.ts +0 -404
  269. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +0 -108
  270. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +0 -561
  271. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +0 -119
  272. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +0 -149
  273. package/src/lib/generators/__tests__/module-override-targets.test.ts +0 -304
  274. package/src/lib/generators/module-extension-facts.ts +0 -2365
  275. package/src/lib/generators/module-fact-sources.ts +0 -108
  276. package/src/lib/generators/module-override-targets.ts +0 -863
  277. package/src/lib/load-env.ts +0 -30
  278. package/src/lib/telemetry-init.ts +0 -461
@@ -0,0 +1,108 @@
1
+ # Shared Package — Standalone Developer Guide
2
+
3
+ `@open-mercato/shared` provides cross-cutting utilities, types, and DSL helpers. Import from here for infrastructure concerns — never from `@open-mercato/core`.
4
+
5
+ ## Import Map
6
+
7
+ | Need | Import |
8
+ |------|--------|
9
+ | Client-side translations | `import { useT } from '@open-mercato/shared/lib/i18n/context'` |
10
+ | Server-side translations | `import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'` |
11
+ | Encrypted queries | `import { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'` |
12
+ | Boolean parsing | `import { parseBooleanToken, parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'` |
13
+ | Command pattern | `import { registerCommand } from '@open-mercato/shared/lib/commands'` |
14
+ | Safe entity flush | `import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'` |
15
+ | Data/Query engine types | `import type { DataEngine, QueryEngine } from '@open-mercato/shared/lib/data/engine'` |
16
+ | CRUD multi-ID filtering | `import { parseIdsParam, mergeIdFilter } from '@open-mercato/shared/lib/crud/ids'` |
17
+ | CRUD OpenAPI factory | `import { createCrudOpenApiFactory } from '@open-mercato/shared/lib/openapi/crud'` |
18
+ | Scoped API payloads | `import { withScopedPayload } from '@open-mercato/shared/lib/api/scoped'` |
19
+ | DI setup (Awilix) | `import { ... } from '@open-mercato/shared/lib/di'` |
20
+ | Custom field helpers | `import { splitCustomFieldPayload, normalizeCustomFieldValues } from '@open-mercato/shared/lib/custom-fields'` |
21
+ | DSL helpers | `import { defineLink, entityId, cf } from '@open-mercato/shared/modules/dsl'` |
22
+ | Event declarations | `import { createModuleEvents } from '@open-mercato/shared/modules/events'` |
23
+ | Module setup types | `import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'` |
24
+ | Search config types | `import type { SearchModuleConfig } from '@open-mercato/shared/modules/search'` |
25
+ | Widget injection position | `import { InjectionPosition } from '@open-mercato/shared/modules/widgets/injection-position'` |
26
+ | API interceptor types | `import type { ApiInterceptor } from '@open-mercato/shared/lib/crud/api-interceptor'` |
27
+ | Response enricher types | `import type { ResponseEnricher } from '@open-mercato/shared/lib/crud/response-enricher'` |
28
+ | Broadcast event check | `import { isBroadcastEvent } from '@open-mercato/shared/modules/events'` |
29
+
30
+ ## i18n — All User-Facing Strings
31
+
32
+ Never hard-code user-facing text. Use locale files and translation helpers.
33
+
34
+ ```typescript
35
+ // Client-side (React components)
36
+ const t = useT()
37
+ return <span>{t('my_module.labels.title')}</span>
38
+
39
+ // Server-side (API routes, commands)
40
+ const { t } = await resolveTranslations()
41
+ const label = t('my_module.labels.title')
42
+ ```
43
+
44
+ Add translations to `i18n/<locale>.json` files in your module.
45
+
46
+ ## Encryption — Query Encrypted Entities
47
+
48
+ MUST use these instead of raw `em.find`/`em.findOne` when the entity may contain encrypted fields:
49
+
50
+ ```typescript
51
+ const results = await findWithDecryption(em, 'Entity', filter, { tenantId, organizationId })
52
+ const record = await findOneWithDecryption(em, 'Entity', filter, { tenantId, organizationId })
53
+ ```
54
+
55
+ ## Boolean Parsing
56
+
57
+ For env vars and query params, never use `=== 'true'`:
58
+
59
+ ```typescript
60
+ const isEnabled = parseBooleanToken(process.env.MY_FLAG) // true | false | undefined
61
+ const withDefault = parseBooleanWithDefault(query.active, true) // defaults to true
62
+ ```
63
+
64
+ ## Safe Entity Flush
65
+
66
+ When a command mutates entities across phases that include queries on the same `EntityManager`, use `withAtomicFlush` to prevent silent data loss:
67
+
68
+ ```typescript
69
+ import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
70
+
71
+ await withAtomicFlush(em, [
72
+ () => { record.name = 'New'; record.status = 'active' },
73
+ () => syncEntityTags(em, record, tags), // internal em.find() won't lose changes
74
+ ], { transaction: true })
75
+
76
+ // Side effects AFTER the atomic flush
77
+ await emitCrudSideEffects({ ... })
78
+ ```
79
+
80
+ **Never** run `em.find`/`em.findOne` between scalar mutations and `em.flush()` without `withAtomicFlush`.
81
+
82
+ ## CRUD Multi-ID Filtering
83
+
84
+ Filter list APIs by multiple IDs using `?ids=uuid1,uuid2`:
85
+
86
+ ```typescript
87
+ import { parseIdsParam, mergeIdFilter } from '@open-mercato/shared/lib/crud/ids'
88
+ const ids = parseIdsParam(query.ids) // string[] | undefined
89
+ const filter = mergeIdFilter(existingFilter, ids) // intersects with existing id filter
90
+ ```
91
+
92
+ ## Custom Field Helpers
93
+
94
+ When your entity uses custom fields:
95
+
96
+ ```typescript
97
+ import { splitCustomFieldPayload, normalizeCustomFieldValues, normalizeCustomFieldResponse }
98
+ from '@open-mercato/shared/lib/custom-fields'
99
+
100
+ // In create/update: split cf:* fields from standard fields
101
+ const { standard, custom } = splitCustomFieldPayload(body)
102
+
103
+ // Normalize for storage
104
+ const normalized = normalizeCustomFieldValues(custom, fieldDefinitions)
105
+
106
+ // Normalize for API response
107
+ const response = normalizeCustomFieldResponse(record, customFields)
108
+ ```
@@ -0,0 +1,347 @@
1
+ # UI Package — Standalone Developer Guide
2
+
3
+ `@open-mercato/ui` provides all admin/backend UI components. Use these instead of building from scratch.
4
+
5
+ ## Key Imports
6
+
7
+ ```typescript
8
+ // Buttons (MUST use — never raw <button>)
9
+ import { Button } from '@open-mercato/ui/primitives/button'
10
+ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
11
+
12
+ // CRUD forms
13
+ import { CrudForm, createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/crud'
14
+ import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
15
+
16
+ // API calls (MUST use — never raw fetch)
17
+ import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
18
+
19
+ // Page structure
20
+ import { FormHeader, FormFooter } from '@open-mercato/ui/backend/forms'
21
+ import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
22
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
23
+
24
+ // Widget injection
25
+ import { useInjectionDataWidgets } from '@open-mercato/ui/backend/injection/useInjectionDataWidgets'
26
+ import { useInjectedMenuItems } from '@open-mercato/ui/backend/injection/useInjectedMenuItems'
27
+ import { mergeMenuItems } from '@open-mercato/ui/backend/injection/mergeMenuItems'
28
+ import { useRegisteredComponent } from '@open-mercato/ui/backend/injection/useRegisteredComponent'
29
+ import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
30
+
31
+ // Real-time events
32
+ import { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'
33
+ import { useOperationProgress } from '@open-mercato/ui/backend/injection/useOperationProgress'
34
+
35
+ // Custom fields
36
+ import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
37
+ ```
38
+
39
+ ## MUST Rules
40
+
41
+ 1. **MUST use `Button`/`IconButton`** — never raw `<button>` elements
42
+ 2. **MUST pass `type="button"` explicitly** on non-submit buttons (HTML defaults to `type="submit"`)
43
+ 3. **MUST use `apiCall`/`apiCallOrThrow`** — never raw `fetch`
44
+ 4. **MUST use `LoadingMessage`/`ErrorMessage`** for loading/error states
45
+ 5. **MUST NOT hard-code user-facing strings** — use `useT()` for all labels
46
+ 6. **Every dialog**: `Cmd/Ctrl+Enter` to submit, `Escape` to cancel
47
+ 7. **Keep `pageSize`** at or below 100
48
+
49
+ ## CrudForm — Create/Edit Flows
50
+
51
+ Default for all create/edit pages and dialog forms:
52
+
53
+ ```tsx
54
+ <CrudForm
55
+ entityId="my_module:my_entity"
56
+ mode="create" // or "edit"
57
+ fields={fields}
58
+ groups={groups}
59
+ onSubmit={async (data) => {
60
+ await createCrud('/api/my-module/items', data)
61
+ flash(t('my_module.created'), 'success')
62
+ }}
63
+ />
64
+ ```
65
+
66
+ - Drive validation with Zod schemas; surface errors via `createCrudFormError(message, fieldErrors?)`
67
+ - Pass `entityIds` when custom fields are involved
68
+ - Use `createCrud`/`updateCrud`/`deleteCrud` for submit actions
69
+ - Keep fields and groups in memoized helpers
70
+
71
+ ## Non-CrudForm Write Operations
72
+
73
+ When a page can't use `CrudForm`, wrap every write in `useGuardedMutation`:
74
+
75
+ ```typescript
76
+ const { runMutation } = useGuardedMutation()
77
+ await runMutation({ operation: 'update', context: { entityId, recordId }, mutationPayload: data })
78
+ ```
79
+
80
+ ## DataTable — List Views
81
+
82
+ ```tsx
83
+ <DataTable
84
+ columns={columns}
85
+ data={items}
86
+ extensionTableId="my-module-items" // stable ID for widget injection
87
+ rowClickActionIds={['edit', 'open']} // default
88
+ />
89
+ ```
90
+
91
+ - Set `meta.truncate` and `meta.maxWidth` on columns for truncation
92
+ - Use `FilterBar`/`FilterOverlay` with async option loaders
93
+ - Use stable `extensionTableId` — widget injection spots depend on it
94
+ - Extension spots: `data-table:<tableId>:columns`, `:row-actions`, `:bulk-actions`, `:filters`
95
+
96
+ ## Buttons — Quick Reference
97
+
98
+ | Use case | Component | Example |
99
+ |----------|-----------|---------|
100
+ | Button with text | `Button` | Save, Cancel, Create |
101
+ | Icon-only button | `IconButton` | Close, Settings, Trash |
102
+ | Link as button | `IconButton asChild` | `<IconButton asChild><Link href="...">...</Link></IconButton>` |
103
+
104
+ **Button variants**: `default`, `destructive`, `outline`, `secondary`, `ghost`, `muted`, `link`
105
+ **IconButton variants**: `outline` (default), `ghost`
106
+
107
+ ## FormHeader Modes
108
+
109
+ ```tsx
110
+ // Edit mode (compact, used by CrudForm)
111
+ <FormHeader mode="edit" title="Edit Item" />
112
+
113
+ // Detail mode (large title with status badge)
114
+ <FormHeader mode="detail" title="Item #123" entityTypeLabel="Item" statusBadge={<Badge>Active</Badge>} menuActions={[...]} />
115
+ ```
116
+
117
+ ## Menu Injection
118
+
119
+ Load and merge injected menu items for sidebar/topbar surfaces:
120
+
121
+ ```typescript
122
+ const injectedItems = useInjectedMenuItems('menu:sidebar:main')
123
+ const merged = mergeMenuItems(builtInItems, injectedItems)
124
+ ```
125
+
126
+ Use `InjectionPosition` + `relativeTo` for deterministic placement.
127
+
128
+ ## Real-Time Events
129
+
130
+ ```typescript
131
+ // Subscribe to server events (via DOM Event Bridge)
132
+ useAppEvent('my_module.entity.*', (event) => {
133
+ // event.id, event.payload — refresh data, show notifications
134
+ }, [dependencies])
135
+
136
+ // Track long-running operations
137
+ const progress = useOperationProgress('my_module.import.*')
138
+ // progress.status, progress.progress (0-100), progress.processedCount
139
+ ```
140
+
141
+ ## Flash Messages
142
+
143
+ ```typescript
144
+ flash(t('my_module.saved'), 'success')
145
+ flash(t('my_module.error'), 'error')
146
+ ```
147
+
148
+ ## Component Replacement
149
+
150
+ For replacement-aware surfaces, resolve components by handle:
151
+
152
+ ```typescript
153
+ const MyComponent = useRegisteredComponent('page:my-module:detail', DefaultDetailPage)
154
+ return <MyComponent {...props} />
155
+ ```
156
+
157
+ ## Portal UI
158
+
159
+ ### Key Imports
160
+
161
+ ```typescript
162
+ // Portal hooks
163
+ import { useCustomerAuth } from '@open-mercato/ui/portal/hooks/useCustomerAuth'
164
+ import { useTenantContext } from '@open-mercato/ui/portal/hooks/useTenantContext'
165
+ import { usePortalAppEvent } from '@open-mercato/ui/portal/hooks/usePortalAppEvent'
166
+ import { usePortalEventBridge } from '@open-mercato/ui/portal/hooks/usePortalEventBridge'
167
+ import { usePortalInjectedMenuItems } from '@open-mercato/ui/portal/hooks/usePortalInjectedMenuItems'
168
+ import { usePortalNotifications } from '@open-mercato/ui/portal/hooks/usePortalNotifications'
169
+ import { usePortalDashboardWidgets } from '@open-mercato/ui/portal/hooks/usePortalDashboardWidgets'
170
+
171
+ // Portal layout
172
+ import { PortalShell } from '@open-mercato/ui/portal/PortalShell'
173
+ import { PortalProvider, usePortalContext } from '@open-mercato/ui/portal/PortalContext'
174
+
175
+ // Portal components
176
+ import {
177
+ PortalCard, PortalCardHeader, PortalStatRow, PortalCardDivider,
178
+ PortalPageHeader, PortalEmptyState, PortalFeatureCard,
179
+ PortalNotificationBell, PortalNotificationPanel,
180
+ } from '@open-mercato/ui/portal/components'
181
+ ```
182
+
183
+ ### Portal Hooks Reference
184
+
185
+ | Hook | Purpose |
186
+ |------|---------|
187
+ | `useCustomerAuth(orgSlug?)` | Customer auth state: `{ user, roles, resolvedFeatures, isPortalAdmin, loading, error, logout }` |
188
+ | `useTenantContext(orgSlug)` | Resolve tenant/org from URL slug: `{ tenantId, organizationId, organizationName, loading, error }` |
189
+ | `usePortalAppEvent(pattern, handler, deps?)` | Listen for portal SSE events by glob pattern (e.g., `'sales.order.*'`) |
190
+ | `usePortalEventBridge()` | Establish singleton SSE connection — mount once in shell/layout |
191
+ | `usePortalInjectedMenuItems(surfaceId)` | Load feature-gated menu items for portal nav surfaces: `{ items, isLoading }` |
192
+ | `usePortalNotifications()` | Poll portal notifications: `{ notifications, unreadCount, hasNew, isLoading, refresh, markAsRead, dismiss, markAllRead }` |
193
+ | `usePortalDashboardWidgets(spotId)` | Load UI injection widgets (with `Widget` component) for a portal spot: `{ widgets, isLoading, error }` |
194
+
195
+ ### Portal Components
196
+
197
+ | Component | Props | Purpose |
198
+ |-----------|-------|---------|
199
+ | `PortalCard` | `children, className?` | Card container with border and padding |
200
+ | `PortalCardHeader` | `title, description?, label?, action?` | Card header with optional uppercase label and action slot |
201
+ | `PortalStatRow` | `label, value` | Key-value row inside a card (uppercase label, right-aligned value) |
202
+ | `PortalCardDivider` | — | Horizontal divider between stat rows |
203
+ | `PortalPageHeader` | `title, description?, label?, action?` | Page-level header with large title and action slot |
204
+ | `PortalEmptyState` | `title, description?, icon?, action?` | Dashed-border empty state with optional icon and CTA |
205
+ | `PortalFeatureCard` | `title, description?, icon?, href?, onClick?` | Feature grid card — renders as link, button, or static div |
206
+ | `PortalNotificationBell` | `t` | Header bell icon with unread badge |
207
+ | `PortalNotificationPanel` | — | Notification dropdown panel |
208
+
209
+ ### Portal Page Structure
210
+
211
+ Every portal page is two files under `frontend/[orgSlug]/portal/<path>/`:
212
+
213
+ ```
214
+ page.tsx # Client component ("use client")
215
+ page.meta.ts # PageMetadata — access control + sidebar nav
216
+ ```
217
+
218
+ Minimal page:
219
+
220
+ ```tsx
221
+ "use client"
222
+ import { usePortalContext } from '@open-mercato/ui/portal/PortalContext'
223
+ import { PortalPageHeader } from '@open-mercato/ui/portal/components'
224
+
225
+ export default function MyPortalPage({ params }: { params: { orgSlug: string } }) {
226
+ const { auth } = usePortalContext()
227
+ const { user, resolvedFeatures } = auth
228
+ return <PortalPageHeader title="Orders" />
229
+ }
230
+ ```
231
+
232
+ Prefer `usePortalContext()` inside pages wrapped by `PortalLayoutShell` — it reads server-hydrated auth and avoids client loading flashes. Reach for `useCustomerAuth(orgSlug)` only when the server wrapper is unavailable.
233
+
234
+ Minimal `page.meta.ts`:
235
+
236
+ ```ts
237
+ import type { PageMetadata } from '@open-mercato/shared/modules/registry'
238
+
239
+ export const metadata: PageMetadata = {
240
+ requireCustomerAuth: true,
241
+ requireCustomerFeatures: ['portal.orders.view'],
242
+ titleKey: 'portal.orders.title',
243
+ title: 'Orders',
244
+ nav: { label: 'Orders', labelKey: 'portal.nav.orders', group: 'main', order: 20 },
245
+ }
246
+
247
+ export default metadata
248
+ ```
249
+
250
+ - Public pages (login, signup, verify, forgot/reset-password): omit `requireCustomerAuth`; set `navHidden: true`.
251
+ - Authenticated pages without sidebar presence (detail/create/edit): set `requireCustomerAuth: true`, **omit** `nav`.
252
+ - Sidebar-visible pages: include a `nav` block. Feature-gated pages are automatically hidden when the user lacks grants.
253
+
254
+ Reference: `packages/core/src/modules/portal/frontend/[orgSlug]/portal/{dashboard,profile}/page.{tsx,meta.ts}`.
255
+
256
+ ### Portal Feature-Gating Contract
257
+
258
+ Single source of truth: `requireCustomerFeatures` in `page.meta.ts`. The same list is enforced in three layers:
259
+
260
+ | Layer | Where | Effect |
261
+ |---|---|---|
262
+ | Page access | `apps/mercato/src/app/(frontend)/[...slug]/page.tsx` | Server-side gate via `CustomerRbacService.userHasAllFeatures()` — missing feature blocks render |
263
+ | Sidebar entry | `/api/customer_accounts/portal/nav` → `buildPortalNav()` at `packages/ui/src/portal/utils/nav.ts` | Same check — missing feature omits the entry |
264
+ | Injection widgets | `usePortalInjectedMenuItems` / `usePortalDashboardWidgets` | `/api/customer_accounts/portal/feature-check` + `hasAllFeatures()` — missing feature filters the widget |
265
+
266
+ Granting a customer role a feature (e.g. `portal.orders.view`) is sufficient to (a) reach the page, (b) see the sidebar entry, (c) see widgets gated by that feature. No separate menu-injection widget is required for sidebar presence when the page is backed by `page.meta.ts` with a `nav` block.
267
+
268
+ **MUST** resolve features via `hasAllFeatures` / `matchFeature` from `@open-mercato/shared/security/features`. Raw `Array.includes()` or `Set.has()` on feature arrays misses wildcards (`portal.*`) and is a bug.
269
+
270
+ Declare features in `acl.ts`; ship defaults per role via `defaultCustomerRoleFeatures` in `setup.ts`. Never rely on client-side checks alone as the access gate.
271
+
272
+ ### Portal SPA CSRF Posture
273
+
274
+ Dual cookies set by login (`packages/core/src/modules/customer_accounts/api/login.ts`):
275
+
276
+ | Cookie | Contents | TTL | Flags |
277
+ |---|---|---|---|
278
+ | `customer_auth_token` | Short-lived JWT | 8h | `httpOnly`, `sameSite: 'lax'`, `secure` in prod, `path: '/'` |
279
+ | `customer_session_token` | Raw session token (hashed at rest) | 30d (env: `CUSTOMER_SESSION_TTL_DAYS`) | same as above |
280
+
281
+ Primary CSRF defense: `SameSite=lax` + same-origin deployment. No explicit CSRF token — the browser blocks cross-origin POSTs.
282
+
283
+ Rules:
284
+ - Use `apiCall` for every write — it uses `credentials: 'same-origin'` and sets JSON headers.
285
+ - Never expose either cookie to JS. `httpOnly` is load-bearing; do not add companion cookies that mirror session state.
286
+ - Never accept cross-origin POSTs on portal routes. Cross-origin use cases are explicit exceptions: per-tenant origin allowlist + CSRF token + re-auth.
287
+ - `sameSite: 'lax'` lets GET navigations carry cookies — keep all state-changing side effects behind POST/PUT/PATCH/DELETE.
288
+ - Logout (`api/portal/logout.ts`) clears both cookies with `maxAge: 0`. Mirror this shape for any new logout-style endpoints.
289
+
290
+ Concurrent sessions are capped at `MAX_CUSTOMER_SESSIONS_PER_USER` (default 5) in `customerSessionService.createSession()`. New sessions above the cap soft-delete the oldest active session.
291
+
292
+ ### Portal XSS Discipline (Injected Widgets)
293
+
294
+ Third-party widgets render inside the authenticated portal and inherit user cookies. Enforce stricter discipline than first-party code because widgets load from arbitrary modules.
295
+
296
+ - **Forbidden**: `dangerouslySetInnerHTML` anywhere in portal injection widgets. Render structured data, not raw HTML.
297
+ - **Labels and user-facing text**: always through `useT()`; render as text children, never as HTML.
298
+ - **Icons**: Lucide components (`lucide-react`). No inline `<svg>` composed from user-controlled strings.
299
+ - **Asset URLs** (`src`, `href`, `action`, `srcDoc`): must not be user-controlled unless validated server-side against an allowlist.
300
+ - **No `eval`, `new Function`, `setTimeout(string)`**, or similar dynamic code paths.
301
+ - **Event-handler payloads** from SSE: validate shape (`isPortalBroadcastEvent` guards dispatch; never trust `event.data` to be well-formed without schema validation).
302
+ - **Styles**: no user-controlled strings in `style` props, CSS variables, or `className` built from untrusted input.
303
+
304
+ Prefer components that accept structured props over ones accepting `children` / `innerHTML` — the host keeps control of escaping.
305
+
306
+ ### PortalShell Usage
307
+
308
+ ```tsx
309
+ import { PortalShell } from '@open-mercato/ui/portal/PortalShell'
310
+ import { useCustomerAuth } from '@open-mercato/ui/portal/hooks/useCustomerAuth'
311
+
312
+ function MyPortalPage({ orgSlug }: { orgSlug: string }) {
313
+ const { user, logout } = useCustomerAuth(orgSlug)
314
+ return (
315
+ <PortalShell orgSlug={orgSlug} authenticated={!!user} onLogout={logout} enableEventBridge>
316
+ <PortalPageHeader title="My Orders" description="View and track your orders" />
317
+ {/* page content */}
318
+ </PortalShell>
319
+ )
320
+ }
321
+ ```
322
+
323
+ When `PortalProvider` is mounted in a parent layout, `PortalShell` reads auth/tenant state from context automatically — props act as overrides or are used on public pages without a provider.
324
+
325
+ ### Portal Widget Injection Spots (FROZEN)
326
+
327
+ | Spot ID | Purpose |
328
+ |---------|---------|
329
+ | `menu:portal:sidebar:main` | Main portal navigation items |
330
+ | `menu:portal:sidebar:account` | Account/settings navigation items |
331
+ | `menu:portal:header:actions` | Header action buttons |
332
+ | `menu:portal:user-dropdown` | User dropdown menu items |
333
+ | `portal:dashboard:sections` | Dashboard section cards |
334
+ | `portal:dashboard:profile` | Dashboard profile area |
335
+ | `portal:dashboard:sidebar` | Dashboard sidebar |
336
+ | `portal:<pageId>:before` | Before page content |
337
+ | `portal:<pageId>:after` | After page content |
338
+
339
+ ### Portal Component Replacement Handles (FROZEN)
340
+
341
+ | Handle | Constant | Purpose |
342
+ |--------|----------|---------|
343
+ | `page:portal:layout` | `PORTAL_SHELL_HANDLE` | Entire portal shell |
344
+ | `section:portal:header` | `PORTAL_HEADER_HANDLE` | Header bar |
345
+ | `section:portal:footer` | `PORTAL_FOOTER_HANDLE` | Footer |
346
+ | `section:portal:sidebar` | `PORTAL_SIDEBAR_HANDLE` | Navigation sidebar |
347
+ | `section:portal:user-menu` | `PORTAL_USER_MENU_HANDLE` | User menu / logout area |