@open-mercato/cli 0.6.7 → 0.6.8-develop.6875.1.871a4afc94

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 +24 -7
  3. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +32 -88
  4. package/dist/agentic/codex/enforcement-rules.md +2 -16
  5. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +19 -50
  6. package/dist/agentic/cursor/rules/entity-guard.mdc +9 -21
  7. package/dist/agentic/cursor/rules/generated-guard.mdc +3 -5
  8. package/dist/agentic/cursor/rules/open-mercato.mdc +3 -29
  9. package/dist/agentic/guides/ai-workflows.md +76 -0
  10. package/dist/agentic/guides/architecture.md +107 -0
  11. package/dist/agentic/guides/backend-ui.md +80 -0
  12. package/dist/agentic/guides/contracts.md +92 -0
  13. package/dist/agentic/guides/extensions.md +121 -0
  14. package/dist/agentic/guides/framework-extension-points.md +36 -0
  15. package/dist/agentic/guides/integrations.md +81 -0
  16. package/dist/agentic/guides/module-facts.json +115537 -3951
  17. package/dist/agentic/guides/module-system.md +12 -127
  18. package/dist/agentic/guides/modules/ai_assistant.md +227 -28
  19. package/dist/agentic/guides/modules/api_docs.md +58 -2
  20. package/dist/agentic/guides/modules/api_keys.md +92 -7
  21. package/dist/agentic/guides/modules/attachments.md +119 -14
  22. package/dist/agentic/guides/modules/audit_logs.md +131 -10
  23. package/dist/agentic/guides/modules/auth.md +276 -33
  24. package/dist/agentic/guides/modules/business_rules.md +149 -14
  25. package/dist/agentic/guides/modules/catalog.md +475 -43
  26. package/dist/agentic/guides/modules/channel_gmail.md +82 -3
  27. package/dist/agentic/guides/modules/channel_imap.md +82 -3
  28. package/dist/agentic/guides/modules/checkout.md +299 -27
  29. package/dist/agentic/guides/modules/communication_channels.md +315 -33
  30. package/dist/agentic/guides/modules/configs.md +104 -8
  31. package/dist/agentic/guides/modules/content.md +57 -2
  32. package/dist/agentic/guides/modules/currencies.md +152 -17
  33. package/dist/agentic/guides/modules/customer_accounts.md +387 -45
  34. package/dist/agentic/guides/modules/customers.md +746 -85
  35. package/dist/agentic/guides/modules/dashboards.md +155 -10
  36. package/dist/agentic/guides/modules/data_sync.md +149 -16
  37. package/dist/agentic/guides/modules/design_system.md +102 -0
  38. package/dist/agentic/guides/modules/dictionaries.md +107 -12
  39. package/dist/agentic/guides/modules/directory.md +148 -15
  40. package/dist/agentic/guides/modules/entities.md +145 -13
  41. package/dist/agentic/guides/modules/events.md +59 -2
  42. package/dist/agentic/guides/modules/feature_toggles.md +119 -9
  43. package/dist/agentic/guides/modules/gateway_stripe.md +92 -4
  44. package/dist/agentic/guides/modules/generators.md +45 -2
  45. package/dist/agentic/guides/modules/inbox_ops.md +207 -27
  46. package/dist/agentic/guides/modules/integrations.md +146 -16
  47. package/dist/agentic/guides/modules/messages.md +223 -24
  48. package/dist/agentic/guides/modules/notifications.md +96 -8
  49. package/dist/agentic/guides/modules/onboarding.md +77 -6
  50. package/dist/agentic/guides/modules/payment_gateways.md +164 -21
  51. package/dist/agentic/guides/modules/perspectives.md +77 -7
  52. package/dist/agentic/guides/modules/planner.md +136 -18
  53. package/dist/agentic/guides/modules/portal.md +93 -2
  54. package/dist/agentic/guides/modules/progress.md +104 -14
  55. package/dist/agentic/guides/modules/query_index.md +154 -15
  56. package/dist/agentic/guides/modules/record_locks.md +206 -21
  57. package/dist/agentic/guides/modules/resources.md +197 -30
  58. package/dist/agentic/guides/modules/sales.md +678 -84
  59. package/dist/agentic/guides/modules/scheduler.md +133 -14
  60. package/dist/agentic/guides/modules/search.md +170 -6
  61. package/dist/agentic/guides/modules/security.md +294 -35
  62. package/dist/agentic/guides/modules/shipping_carriers.md +134 -18
  63. package/dist/agentic/guides/modules/sso.md +151 -26
  64. package/dist/agentic/guides/modules/staff.md +369 -57
  65. package/dist/agentic/guides/modules/storage_s3.md +95 -5
  66. package/dist/agentic/guides/modules/sync_akeneo.md +85 -3
  67. package/dist/agentic/guides/modules/sync_excel.md +75 -6
  68. package/dist/agentic/guides/modules/system_status_overlays.md +60 -2
  69. package/dist/agentic/guides/modules/translations.md +104 -10
  70. package/dist/agentic/guides/modules/webhooks.md +203 -24
  71. package/dist/agentic/guides/modules/wms.md +441 -46
  72. package/dist/agentic/guides/modules/workflows.md +302 -43
  73. package/dist/agentic/guides/spec-delivery.md +22 -0
  74. package/dist/agentic/guides/testing-debugging.md +80 -0
  75. package/dist/agentic/guides/upstream/AGENTS.md +299 -0
  76. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +316 -0
  77. package/dist/agentic/guides/upstream/manifest.json +8 -0
  78. package/dist/agentic/shared/AGENTS.md.template +124 -311
  79. package/dist/agentic/shared/ai/agentic.config.json +2 -0
  80. package/dist/agentic/shared/ai/harness/README.md +50 -0
  81. package/dist/agentic/shared/ai/harness/RELEASE.md +57 -0
  82. package/dist/agentic/shared/ai/harness/cases.json +13680 -0
  83. package/dist/agentic/shared/ai/harness/cases.schema.json +133 -0
  84. package/dist/agentic/shared/ai/harness/fixtures/index.json +49 -0
  85. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +162 -0
  86. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +21 -0
  87. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +96 -0
  88. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +172 -0
  89. package/dist/agentic/shared/ai/harness/release-matrix.json +158 -0
  90. package/dist/agentic/shared/ai/harness/release-result.schema.json +219 -0
  91. package/dist/agentic/shared/ai/harness/result.schema.json +76 -0
  92. package/dist/agentic/shared/ai/harness/routing-response.schema.json +12 -0
  93. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +73 -0
  94. package/dist/agentic/shared/ai/harness/validators.json +78 -0
  95. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +2121 -0
  96. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +1096 -0
  97. package/dist/agentic/shared/ai/lessons/_template.md +16 -0
  98. package/dist/agentic/shared/ai/lessons.md +26 -3
  99. package/dist/agentic/shared/ai/review-checklist.md +36 -0
  100. package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +21 -0
  101. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +4 -0
  102. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +98 -0
  103. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +20 -241
  104. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +16 -0
  105. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +53 -0
  106. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +13 -0
  107. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +11 -0
  108. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +30 -0
  109. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +12 -0
  110. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +13 -0
  111. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +14 -0
  112. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +28 -0
  113. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +12 -0
  114. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +22 -0
  115. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +12 -0
  116. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +19 -0
  117. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +16 -601
  118. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +19 -0
  119. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +13 -0
  120. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +14 -0
  121. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +10 -0
  122. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +14 -310
  123. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +17 -0
  124. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +29 -0
  125. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +100 -0
  126. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +16 -0
  127. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +17 -0
  128. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +25 -0
  129. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +11 -0
  130. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +13 -0
  131. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +15 -97
  132. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +18 -0
  133. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +20 -0
  134. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +33 -152
  135. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +33 -0
  136. package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +40 -0
  137. package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +27 -0
  138. package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +24 -0
  139. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +24 -708
  140. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +16 -0
  141. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +14 -0
  142. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +15 -0
  143. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +12 -0
  144. package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +34 -0
  145. package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +4 -0
  146. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +11 -0
  147. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +31 -0
  148. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +39 -0
  149. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +43 -0
  150. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +11 -0
  151. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +20 -820
  152. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +19 -0
  153. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +172 -0
  154. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +12 -0
  155. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +83 -0
  156. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +21 -0
  157. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +11 -0
  158. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +22 -0
  159. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +13 -0
  160. package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +41 -0
  161. package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +19 -0
  162. package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +44 -0
  163. package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +39 -0
  164. package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +20 -0
  165. package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +57 -0
  166. package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +11 -0
  167. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +486 -0
  168. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +19 -843
  169. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +18 -0
  170. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +27 -0
  171. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +13 -0
  172. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +41 -0
  173. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +11 -0
  174. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +15 -63
  175. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +15 -0
  176. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +16 -467
  177. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +16 -0
  178. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +14 -0
  179. package/dist/agentic/shared/ai/skills/tiers.json +143 -21
  180. package/dist/agentic/shared/ai/skills/tiers.schema.json +52 -40
  181. package/dist/agentic/shared/ai/specs/README.md +4 -3
  182. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +226 -10
  183. package/dist/agentic/shared/ai/trackers/github.md +108 -4
  184. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +187 -0
  185. package/dist/agentic/shared/scripts/check-lessons.mjs +178 -0
  186. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +2958 -0
  187. package/dist/agentic/shared/scripts/execution-sandbox.mjs +329 -0
  188. package/dist/agentic/shared/scripts/framework-context.mjs +888 -0
  189. package/dist/agentic/shared/scripts/install-skills.mjs +1029 -0
  190. package/dist/agentic/shared/scripts/install-skills.sh +4 -694
  191. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +222 -0
  192. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +1774 -0
  193. package/dist/bin.js +13 -1
  194. package/dist/bin.js.map +2 -2
  195. package/dist/lib/__integration__/TC-INT-008.spec.js +88 -8
  196. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  197. package/dist/lib/agentic-init.js +13 -3
  198. package/dist/lib/agentic-init.js.map +2 -2
  199. package/dist/lib/agentic-setup.js +434 -186
  200. package/dist/lib/agentic-setup.js.map +2 -2
  201. package/dist/lib/generators/extensions/injection-widgets.js +20 -0
  202. package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
  203. package/dist/lib/generators/module-extension-facts.js +1840 -0
  204. package/dist/lib/generators/module-extension-facts.js.map +7 -0
  205. package/dist/lib/generators/module-fact-sources.js +70 -0
  206. package/dist/lib/generators/module-fact-sources.js.map +7 -0
  207. package/dist/lib/generators/module-facts-discovery.js +41 -7
  208. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  209. package/dist/lib/generators/module-facts.js +1387 -45
  210. package/dist/lib/generators/module-facts.js.map +3 -3
  211. package/dist/lib/generators/module-override-targets.js +688 -0
  212. package/dist/lib/generators/module-override-targets.js.map +7 -0
  213. package/dist/lib/generators/module-registry.js +11 -5
  214. package/dist/lib/generators/module-registry.js.map +2 -2
  215. package/dist/lib/load-env.js +24 -0
  216. package/dist/lib/load-env.js.map +7 -0
  217. package/dist/lib/resolver.js +2 -0
  218. package/dist/lib/resolver.js.map +2 -2
  219. package/dist/lib/telemetry-init.js +358 -0
  220. package/dist/lib/telemetry-init.js.map +7 -0
  221. package/dist/lib/testing/integration.js +5 -0
  222. package/dist/lib/testing/integration.js.map +2 -2
  223. package/dist/mercato.js +23 -1
  224. package/dist/mercato.js.map +2 -2
  225. package/package.json +13 -11
  226. package/src/bin.ts +33 -1
  227. package/src/lib/__integration__/TC-INT-008.spec.ts +107 -9
  228. package/src/lib/__tests__/agentic-init.test.ts +41 -5
  229. package/src/lib/__tests__/agentic-setup.ownership.test.ts +386 -0
  230. package/src/lib/__tests__/injection-widget-id-validation.test.ts +184 -0
  231. package/src/lib/__tests__/load-env.test.ts +65 -0
  232. package/src/lib/__tests__/telemetry-init.test.ts +298 -0
  233. package/src/lib/agentic-init.ts +15 -3
  234. package/src/lib/agentic-setup.ts +573 -222
  235. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +92 -0
  236. package/src/lib/generators/__tests__/module-extension-activations.test.ts +762 -0
  237. package/src/lib/generators/__tests__/module-extension-facts.test.ts +404 -0
  238. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +88 -2
  239. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -0
  240. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +54 -10
  241. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +108 -0
  242. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +561 -0
  243. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +119 -0
  244. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +149 -0
  245. package/src/lib/generators/__tests__/module-override-targets.test.ts +304 -0
  246. package/src/lib/generators/__tests__/module-subset.test.ts +18 -0
  247. package/src/lib/generators/extensions/injection-widgets.ts +51 -0
  248. package/src/lib/generators/module-extension-facts.ts +2365 -0
  249. package/src/lib/generators/module-fact-sources.ts +108 -0
  250. package/src/lib/generators/module-facts-discovery.ts +58 -7
  251. package/src/lib/generators/module-facts.ts +1851 -46
  252. package/src/lib/generators/module-override-targets.ts +863 -0
  253. package/src/lib/generators/module-registry.ts +16 -6
  254. package/src/lib/load-env.ts +30 -0
  255. package/src/lib/resolver.ts +3 -0
  256. package/src/lib/telemetry-init.ts +461 -0
  257. package/src/lib/testing/__tests__/integration.test.ts +4 -0
  258. package/src/lib/testing/integration.ts +5 -0
  259. package/src/mercato.ts +26 -1
  260. package/dist/agentic/guides/cache.md +0 -45
  261. package/dist/agentic/guides/core.md +0 -453
  262. package/dist/agentic/guides/events.md +0 -117
  263. package/dist/agentic/guides/queue.md +0 -53
  264. package/dist/agentic/guides/search.md +0 -115
  265. package/dist/agentic/guides/shared.md +0 -108
  266. package/dist/agentic/guides/ui.md +0 -347
  267. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +0 -27
  268. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +0 -27
  269. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +0 -260
  270. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +0 -146
  271. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +0 -84
  272. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +0 -193
  273. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +0 -34
  274. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +0 -762
  275. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +0 -50
  276. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +0 -97
  277. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +0 -271
  278. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +0 -101
package/src/mercato.ts CHANGED
@@ -867,7 +867,10 @@ async function buildAllModules(): Promise<Module[]> {
867
867
  export async function run(argv = process.argv) {
868
868
  const [, , ...parts] = argv
869
869
  const [first, second, ...remaining] = parts
870
- await ensureEnvLoaded({ createIfMissing: first !== 'deploy', quiet: first === 'deploy' })
870
+ await ensureEnvLoaded({
871
+ createIfMissing: first !== 'deploy' && first !== 'telemetry',
872
+ quiet: first === 'deploy' || first === 'telemetry',
873
+ })
871
874
 
872
875
  // Handle init command directly
873
876
  if (first === 'init') {
@@ -1332,6 +1335,20 @@ export async function run(argv = process.argv) {
1332
1335
  return exitCode
1333
1336
  }
1334
1337
 
1338
+ // Handle telemetry command (bootstrap-free) — wires @open-mercato/telemetry
1339
+ // into an app scaffolded before telemetry existed.
1340
+ if (first === 'telemetry') {
1341
+ if (second === 'init') {
1342
+ const { runTelemetryInit } = await import('./lib/telemetry-init')
1343
+ return runTelemetryInit(remaining.filter(Boolean))
1344
+ }
1345
+ console.log('Usage: yarn mercato telemetry init [--dry-run]')
1346
+ console.log(' Wires @open-mercato/telemetry into this app (package.json, .env,')
1347
+ console.log(' instrumentation.ts, next.config.ts, and the API dispatcher).')
1348
+ console.log(' Idempotent and safe to re-run. Use --dry-run to preview changes.')
1349
+ return second && second !== 'help' && second !== '--help' && second !== '-h' ? 1 : 0
1350
+ }
1351
+
1335
1352
  if (first === 'module') {
1336
1353
  try {
1337
1354
  const subcommand = second
@@ -1654,6 +1671,8 @@ export async function run(argv = process.argv) {
1654
1671
  effectiveByQueue.get(queue) ??
1655
1672
  concurrencyOverride ??
1656
1673
  Math.max(...queueWorkers.map((w) => w.concurrency), 1)
1674
+ const maxStalledCount = Math.max(...queueWorkers.map((w) => w.maxStalledCount ?? 1), 1)
1675
+ const lockDuration = Math.max(...queueWorkers.map((w) => w.lockDuration ?? 0), 0) || undefined
1657
1676
 
1658
1677
  console.log(`[worker] Starting "${queue}" with ${queueWorkers.length} handler(s), concurrency: ${concurrency}`)
1659
1678
 
@@ -1662,6 +1681,8 @@ export async function run(argv = process.argv) {
1662
1681
  queueName: queue,
1663
1682
  connection: queueRedisUrl ? { url: queueRedisUrl } : undefined,
1664
1683
  concurrency,
1684
+ lockDuration,
1685
+ maxStalledCount,
1665
1686
  background: true,
1666
1687
  handler: createPerJobWorkerHandler(queueWorkers, createRequestContainer),
1667
1688
  })
@@ -1705,6 +1726,8 @@ export async function run(argv = process.argv) {
1705
1726
  // never check out more pooled connections than the worker pool holds.
1706
1727
  const budgetPlan = await resolveWorkerBudgetPlan([{ queue: queueName!, concurrency: requested }])
1707
1728
  const concurrency = budgetPlan.entries[0]?.effective ?? requested
1729
+ const maxStalledCount = Math.max(...queueWorkers.map((w) => w.maxStalledCount ?? 1), 1)
1730
+ const lockDuration = Math.max(...queueWorkers.map((w) => w.lockDuration ?? 0), 0) || undefined
1708
1731
 
1709
1732
  console.log(`[worker] Found ${queueWorkers.length} worker(s) for queue "${queueName}"`)
1710
1733
 
@@ -1713,6 +1736,8 @@ export async function run(argv = process.argv) {
1713
1736
  queueName: queueName!,
1714
1737
  connection: queueRedisUrl ? { url: queueRedisUrl } : undefined,
1715
1738
  concurrency,
1739
+ lockDuration,
1740
+ maxStalledCount,
1716
1741
  handler: createPerJobWorkerHandler(queueWorkers, createRequestContainer),
1717
1742
  })
1718
1743
  } else {
@@ -1,45 +0,0 @@
1
- # Cache Package — Standalone Developer Guide
2
-
3
- `@open-mercato/cache` provides tenant-scoped caching with tag-based invalidation. MUST NOT use raw Redis, SQLite, or in-memory caching directly.
4
-
5
- ## Strategy Selection
6
-
7
- | Strategy | When | Config |
8
- |----------|------|--------|
9
- | Memory | Development, single-process | Default (no config) |
10
- | SQLite | Single-server production | `CACHE_STRATEGY=sqlite` |
11
- | Redis | Multi-server, shared cache | `CACHE_STRATEGY=redis` |
12
-
13
- ## Usage
14
-
15
- Always resolve via DI:
16
-
17
- ```typescript
18
- const cacheService = container.resolve('cacheService')
19
-
20
- // Set with tags for targeted invalidation
21
- await cacheService.set(`${tenantId}:my_module:stats`, value, {
22
- tags: [`tenant:${tenantId}`, 'my_module'],
23
- })
24
-
25
- // Get
26
- const cached = await cacheService.get(`${tenantId}:my_module:stats`)
27
-
28
- // Invalidate by tag (clears all entries with that tag)
29
- await cacheService.invalidateTag('my_module')
30
- ```
31
-
32
- ## MUST Rules
33
-
34
- 1. **MUST resolve via DI** — `container.resolve('cacheService')`, never instantiate directly
35
- 2. **MUST scope to tenant** — include `tenantId` in cache keys
36
- 3. **MUST use tag-based invalidation** for CRUD side effects
37
- 4. **MUST NOT cache sensitive data** without encryption
38
-
39
- ## Adding Cache to a Module
40
-
41
- 1. Resolve `cacheService` from DI in your service or route handler
42
- 2. Define cache keys: `${tenantId}:${module}:${identifier}`
43
- 3. Tag entries: `{ tags: ['tenant:123', 'my_module'] }`
44
- 4. Add cache invalidation to CRUD side effects (`emitCrudSideEffects` with `cacheAliases`)
45
- 5. Test with `CACHE_STRATEGY=memory` (default in dev)
@@ -1,453 +0,0 @@
1
- # Core Package — Standalone Developer Guide
2
-
3
- `@open-mercato/core` contains all built-in business modules. This guide covers module development patterns for standalone apps that build on top of these modules.
4
-
5
- ## Module anatomy, auto-discovery & convention files
6
-
7
- > Moved to the conceptual guide. See **`.ai/guides/module-system.md` → Module anatomy** for the auto-discovery path table and the convention-file reference — they are framework-wide, not core-specific. The sections below cover `@open-mercato/core`-specific development patterns.
8
-
9
- ## API Routes
10
-
11
- Every API route file MUST export an `openApi` object:
12
-
13
- ```typescript
14
- import { createCrudOpenApiFactory } from '@open-mercato/shared/lib/openapi/crud'
15
- const buildOpenApi = createCrudOpenApiFactory({ defaultTag: 'MyModule' })
16
-
17
- export const openApi = buildOpenApi({
18
- resourceName: 'Item',
19
- querySchema: listQuerySchema,
20
- listResponseSchema: createPagedListResponseSchema(itemSchema),
21
- create: { schema: createSchema, description: 'Create item' },
22
- update: { schema: updateSchema, responseSchema: okSchema },
23
- del: { schema: deleteSchema, responseSchema: okSchema },
24
- })
25
- ```
26
-
27
- ### CRUD Routes with makeCrudRoute
28
-
29
- Always set `indexer: { entityType }` for query index coverage:
30
-
31
- ```typescript
32
- makeCrudRoute({
33
- entity: MyEntity,
34
- indexer: { entityType: 'my_module:my_entity' },
35
- enrichers: { entityId: 'my_module.my_entity' }, // opt-in to enrichers
36
- // ...
37
- })
38
- ```
39
-
40
- ### Custom Write Routes
41
-
42
- For non-CRUD write routes (`POST`/`PUT`/`PATCH`/`DELETE`), MUST wire mutation guards:
43
- - Map the route to the closest registry operation (`create`, `update`, or `delete`; state-changing action endpoints usually use `update`)
44
- - Collect registered guards with `getAllMutationGuardInstances()` and append `bridgeLegacyGuard(container)` when present
45
- - Call `runMutationGuards(...)` from `@open-mercato/shared/lib/crud/mutation-guard-registry` before mutation, passing the caller's granted features as `{ userFeatures }`
46
- - Return guard rejection bodies/statuses, merge `modifiedPayload` into validated input when present, and run returned `afterSuccessCallbacks` after successful mutation, catching/logging callback failures so committed writes still return successfully
47
-
48
- ## Module Setup (`setup.ts`)
49
-
50
- Required for tenant initialization:
51
-
52
- ```typescript
53
- import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
54
-
55
- export const setup: ModuleSetupConfig = {
56
- defaultRoleFeatures: {
57
- superadmin: ['my_module.admin_only'],
58
- admin: ['my_module.*'],
59
- employee: ['my_module.view'],
60
- },
61
- async onTenantCreated({ em, tenantId, organizationId }) { /* settings, sequences */ },
62
- async seedDefaults({ em, tenantId, container }) { /* dictionaries, statuses */ },
63
- async seedExamples({ em, tenantId, container }) { /* demo data */ },
64
- }
65
- ```
66
-
67
- ## Events
68
-
69
- Declare events in the emitting module's `events.ts`:
70
-
71
- ```typescript
72
- import { createModuleEvents } from '@open-mercato/shared/modules/events'
73
- const events = [
74
- { id: 'my_mod.item.created', label: 'Item Created', entity: 'item', category: 'crud' },
75
- { id: 'my_mod.item.updated', label: 'Item Updated', entity: 'item', category: 'crud' },
76
- { id: 'my_mod.item.deleted', label: 'Item Deleted', entity: 'item', category: 'crud' },
77
- ] as const
78
- export const eventsConfig = createModuleEvents({ moduleId: 'my_mod', events })
79
- export const emitMyModEvent = eventsConfig.emit
80
- ```
81
-
82
- MUST use `as const` for type safety. Run `yarn generate` after adding.
83
-
84
- ### Subscribers
85
-
86
- ```typescript
87
- // subscribers/item-created-notify.ts
88
- export const metadata = { event: 'my_mod.item.created', persistent: true, id: 'item-created-notify' }
89
- export default async function handler(payload, ctx) { /* one side effect per subscriber */ }
90
- ```
91
-
92
- ## Widget Injection
93
-
94
- The preferred way to extend other modules' UI without direct coupling.
95
-
96
- ### Structure
97
- - Widgets: `widgets/injection/<WidgetName>/widget.tsx` (or `widget.ts` for headless)
98
- - Mapping: `widgets/injection-table.ts`
99
-
100
- ### Spot IDs
101
- - `crud-form:<entityId>` — inject into forms
102
- - `crud-form:<entityId>:fields` — inject form fields
103
- - `data-table:<tableId>:columns|row-actions|bulk-actions|filters` — inject into tables
104
- - `menu:sidebar:main|settings|profile` — sidebar menu items
105
- - `menu:topbar:profile-dropdown|actions` — topbar items
106
-
107
- ### Menu Injection (Headless)
108
- ```typescript
109
- // widgets/injection/MyMenuItem/widget.ts
110
- export const menuItems = [
111
- { id: 'my-mod-dashboard', labelKey: 'my_mod.menu.dashboard', icon: 'lucide:layout-dashboard',
112
- href: '/backend/my-module', placement: { position: InjectionPosition.After, relativeTo: 'customers' } }
113
- ]
114
- ```
115
-
116
- Map in `injection-table.ts`:
117
- ```typescript
118
- export default [{ widgetId: 'MyMenuItem', spots: ['menu:sidebar:main'] }]
119
- ```
120
-
121
- ## Response Enrichers
122
-
123
- Add computed fields to another module's CRUD responses:
124
-
125
- ```typescript
126
- // data/enrichers.ts
127
- export const enrichers: ResponseEnricher[] = [{
128
- id: 'my_mod.customer-stats',
129
- targetEntity: 'customers.person',
130
- features: ['my_mod.view'],
131
- priority: 10,
132
- timeout: 2000,
133
- fallback: { _my_mod: { count: 0 } },
134
- async enrichMany(records, ctx) {
135
- return records.map(r => ({ ...r, _my_mod: { count: 42 } }))
136
- },
137
- }]
138
- ```
139
-
140
- MUST implement `enrichMany()` for batch endpoints. MUST namespace with `_moduleName` prefix.
141
-
142
- ## API Interceptors
143
-
144
- Hook into any route's before/after lifecycle:
145
-
146
- ```typescript
147
- // api/interceptors.ts
148
- export const interceptors: ApiInterceptor[] = [{
149
- id: 'my_mod.narrow-customers',
150
- targetRoute: '/api/customers/people',
151
- methods: ['GET'],
152
- async before(ctx) { /* rewrite query.ids to narrow results */ },
153
- async after(ctx, response) { /* transform response */ },
154
- }]
155
- ```
156
-
157
- ## Access Control (RBAC)
158
-
159
- Declare features in `acl.ts`, guard with metadata:
160
-
161
- ```typescript
162
- // acl.ts
163
- export const features = ['my_module.view', 'my_module.create', 'my_module.edit', 'my_module.delete']
164
- ```
165
-
166
- Always add matching `defaultRoleFeatures` in `setup.ts`.
167
-
168
- Use declarative guards in page metadata: `requireAuth`, `requireRoles`, `requireFeatures`.
169
-
170
- ## Custom Fields & Entities
171
-
172
- Declare in `ce.ts` using DSL helpers:
173
-
174
- ```typescript
175
- import { defineFields, cf } from '@open-mercato/shared/modules/dsl'
176
- export const entities = [{
177
- entityId: 'my_module:my_entity',
178
- fields: defineFields({ fields: [cf.text('notes'), cf.number('priority')] }),
179
- }]
180
- ```
181
-
182
- ## Entity Extensions (Cross-Module Links)
183
-
184
- Extend another module's data without mutating their entities:
185
-
186
- ```typescript
187
- // data/extensions.ts
188
- import { defineLink, entityId, linkable } from '@open-mercato/shared/modules/dsl'
189
- export const extensions = [
190
- defineLink({ source: entityId('my_module:my_entity'), target: linkable('customers:person') })
191
- ]
192
- ```
193
-
194
- ## Component Replacement
195
-
196
- Override or wrap existing UI components:
197
-
198
- ```typescript
199
- // widgets/components.ts
200
- export const componentOverrides = [{
201
- handle: 'page:customers:detail',
202
- mode: 'wrapper', // or 'replace', 'props'
203
- component: MyCustomerDetailWrapper,
204
- }]
205
- ```
206
-
207
- Prefer `wrapper`/`props` modes over full `replace`.
208
-
209
- ## Command Pattern (Write Operations)
210
-
211
- Implement writes via commands for undo/redo support:
212
-
213
- ```typescript
214
- import { registerCommand } from '@open-mercato/shared/lib/commands'
215
- // Reference: @open-mercato/core customers/commands/people.ts
216
- ```
217
-
218
- Include `indexer: { entityType, cacheAliases }` in `emitCrudSideEffects` for query index refresh.
219
-
220
- ## Customer Accounts
221
-
222
- The `customer_accounts` module provides customer-facing authentication, sessions, RBAC, and account management. App modules build on top of it to add portal features.
223
-
224
- ### Auth APIs
225
-
226
- | Endpoint | Purpose |
227
- |----------|---------|
228
- | `POST /api/customer_accounts/login` | Email + password login, returns JWT + session cookies |
229
- | `POST /api/customer_accounts/signup` | Self-registration |
230
- | `POST /api/customer_accounts/magic-link/request` | Request passwordless login link |
231
- | `POST /api/customer_accounts/magic-link/verify` | Verify magic link token |
232
- | `POST /api/customer_accounts/password/reset-request` | Request password reset email |
233
- | `POST /api/customer_accounts/password/reset-confirm` | Confirm password reset with token |
234
- | `POST /api/customer_accounts/email/verify` | Verify email address |
235
- | `POST /api/customer_accounts/invitations/accept` | Accept an invitation token |
236
- | `POST /api/customer_accounts/portal/logout` | Clear customer session |
237
- | `POST /api/customer_accounts/portal/sessions-refresh` | Refresh JWT from session token |
238
- | `POST /api/customer_accounts/portal/password-change` | Change password (authenticated) |
239
-
240
- ### Server-Side Auth Check
241
-
242
- Use `getCustomerAuthFromCookies` in server components and catch-all routes:
243
-
244
- ```typescript
245
- import { getCustomerAuthFromCookies } from '@open-mercato/core/modules/customer_accounts/lib/customerAuthServer'
246
-
247
- const auth = await getCustomerAuthFromCookies()
248
- if (!auth) redirect(`/${orgSlug}/portal/login`)
249
- // auth.sub, auth.email, auth.displayName, auth.resolvedFeatures, auth.customerEntityId
250
- ```
251
-
252
- ### Client-Side Auth
253
-
254
- Use `useCustomerAuth` in portal client components:
255
-
256
- ```typescript
257
- import { useCustomerAuth } from '@open-mercato/ui/portal/hooks/useCustomerAuth'
258
-
259
- const { user, roles, resolvedFeatures, isPortalAdmin, loading, logout } = useCustomerAuth(orgSlug)
260
- ```
261
-
262
- ### Customer RBAC
263
-
264
- Declare `defaultCustomerRoleFeatures` in your module's `setup.ts` to auto-merge features into customer roles during tenant setup:
265
-
266
- ```typescript
267
- import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
268
-
269
- export const setup: ModuleSetupConfig = {
270
- defaultCustomerRoleFeatures: {
271
- buyer: ['portal.my_module.view', 'portal.my_module.create'],
272
- viewer: ['portal.my_module.view'],
273
- },
274
- }
275
- ```
276
-
277
- Built-in customer roles: `portal_admin` (full access), `buyer` (order/quote/catalog), `viewer` (read-only). Features merge additively on `seedDefaults`.
278
-
279
- ### Portal Page Auth Metadata
280
-
281
- Guard portal pages declaratively via page metadata:
282
-
283
- ```typescript
284
- // frontend/[orgSlug]/portal/my-feature/page.meta.ts
285
- export const metadata = {
286
- requireCustomerAuth: true,
287
- requireCustomerFeatures: ['portal.my_module.view'],
288
- navHidden: true,
289
- }
290
- ```
291
-
292
- ### Events
293
-
294
- Subscribe to customer account lifecycle events:
295
-
296
- | Event ID | Category | Broadcast |
297
- |----------|----------|-----------|
298
- | `customer_accounts.user.created` | crud | `clientBroadcast` |
299
- | `customer_accounts.user.updated` | crud | |
300
- | `customer_accounts.user.deleted` | crud | |
301
- | `customer_accounts.user.locked` | lifecycle | |
302
- | `customer_accounts.user.unlocked` | lifecycle | |
303
- | `customer_accounts.login.success` | lifecycle | |
304
- | `customer_accounts.login.failed` | lifecycle | |
305
- | `customer_accounts.email.verified` | lifecycle | |
306
- | `customer_accounts.password.reset` | lifecycle | |
307
- | `customer_accounts.invitation.accepted` | lifecycle | `clientBroadcast` |
308
- | `customer_accounts.role.created` | crud | |
309
- | `customer_accounts.role.updated` | crud | |
310
- | `customer_accounts.role.deleted` | crud | |
311
-
312
- ```typescript
313
- // subscribers/on-customer-signup.ts
314
- export const metadata = { event: 'customer_accounts.user.created', persistent: true, id: 'my-mod-on-customer-signup' }
315
- export default async function handler(payload, ctx) { /* welcome email, CRM sync, etc. */ }
316
- ```
317
-
318
- ### Extending Customer Data
319
-
320
- Link your module's entities to `CustomerUser` via `data/extensions.ts`:
321
-
322
- ```typescript
323
- import { defineLink, entityId, linkable } from '@open-mercato/shared/modules/dsl'
324
- export const extensions = [
325
- defineLink({ source: entityId('my_module:preference'), target: linkable('customer_accounts:user') }),
326
- ]
327
- ```
328
-
329
- Key entities: `CustomerUser` (customer_users), `CustomerRole` (customer_roles), `CustomerRoleAcl`, `CustomerUserRole`, `CustomerUserSession`, `CustomerUserInvitation`.
330
-
331
- ### Admin RBAC Features
332
-
333
- Staff-side features for managing customer accounts:
334
-
335
- | Feature | Purpose |
336
- |---------|---------|
337
- | `customer_accounts.view` | View customer accounts |
338
- | `customer_accounts.manage` | Manage customer accounts |
339
- | `customer_accounts.roles.manage` | Manage customer roles |
340
- | `customer_accounts.invite` | Invite customer users |
341
-
342
- ## Portal Extension
343
-
344
- Build customer-facing portal pages that integrate with the shared portal shell, navigation, and event bridge.
345
-
346
- ### Portal Feature Toggle
347
-
348
- The portal is gated by the `portal_enabled` feature toggle (seeded by the `portal` module). When disabled, all portal routes show a "Portal not available" message.
349
-
350
- ### Portal Page Structure
351
-
352
- Portal pages live at `frontend/[orgSlug]/portal/<feature>/page.tsx` and use the portal shell:
353
-
354
- ```typescript
355
- 'use client'
356
- import { useT } from '@open-mercato/shared/lib/i18n/context'
357
- import { usePortalContext } from '@open-mercato/ui/portal/PortalContext'
358
- import { PortalPageHeader } from '@open-mercato/ui/portal/components/PortalPageHeader'
359
- import { PortalCard, PortalCardHeader } from '@open-mercato/ui/portal/components/PortalCard'
360
-
361
- export default function MyPortalPage({ params }: { params: { orgSlug: string } }) {
362
- const t = useT()
363
- const { auth } = usePortalContext()
364
- const { user, loading } = auth
365
-
366
- if (loading) return <Spinner />
367
- if (!user) return null
368
-
369
- return (
370
- <div className="flex flex-col gap-8">
371
- <PortalPageHeader label={t('my_mod.portal.label')} title={t('my_mod.portal.title')} />
372
- <PortalCard>
373
- <PortalCardHeader label="Section" title="Content" />
374
- {/* page content */}
375
- </PortalCard>
376
- </div>
377
- )
378
- }
379
- ```
380
-
381
- ### Portal Menu Injection
382
-
383
- Inject navigation items into portal chrome surfaces:
384
-
385
- | Spot ID | Purpose |
386
- |---------|---------|
387
- | `menu:portal:sidebar:main` | Main portal navigation |
388
- | `menu:portal:sidebar:account` | Account/settings section |
389
- | `menu:portal:header:actions` | Header action buttons |
390
- | `menu:portal:user-dropdown` | User dropdown items |
391
-
392
- ```typescript
393
- // widgets/injection/PortalNavItem/widget.ts
394
- import { InjectionPosition } from '@open-mercato/shared/modules/widgets/injection-position'
395
-
396
- export const menuItems = [{
397
- id: 'my-mod-portal-nav',
398
- labelKey: 'my_mod.portal.menu.label',
399
- icon: 'lucide:package',
400
- href: '/portal/my-feature',
401
- placement: { position: InjectionPosition.After, relativeTo: 'portal-dashboard' },
402
- }]
403
- ```
404
-
405
- Map in `widgets/injection-table.ts`:
406
- ```typescript
407
- export default { 'menu:portal:sidebar:main': { widgetId: 'my_mod.injection.portal-nav', priority: 10 } }
408
- ```
409
-
410
- ### Portal Widget Injection Spots
411
-
412
- Inject content into portal dashboard and pages:
413
-
414
- | Spot ID | Purpose |
415
- |---------|---------|
416
- | `portal:dashboard:sections` | Dashboard section cards |
417
- | `portal:dashboard:profile` | Dashboard profile area |
418
- | `portal:dashboard:sidebar` | Dashboard sidebar |
419
- | `portal:<pageId>:before` | Before any portal page content |
420
- | `portal:<pageId>:after` | After any portal page content |
421
-
422
- ### Portal Event Bridge
423
-
424
- Events with `portalBroadcast: true` stream to authenticated portal users via SSE at `/api/customer_accounts/portal/events/stream`.
425
-
426
- Declare portal-broadcast events:
427
- ```typescript
428
- // events.ts
429
- const events = [
430
- { id: 'my_mod.item.status_changed', label: 'Item Status Changed', portalBroadcast: true },
431
- ] as const
432
- ```
433
-
434
- Listen in portal components:
435
- ```typescript
436
- import { usePortalAppEvent } from '@open-mercato/ui/portal/hooks/usePortalAppEvent'
437
-
438
- usePortalAppEvent('my_mod.item.status_changed', (event) => { refetch() })
439
- ```
440
-
441
- Enable the bridge in PortalShell with `enableEventBridge` prop.
442
-
443
- ### Portal Component Replacement
444
-
445
- Override portal shell sections via component replacement handles:
446
-
447
- | Handle | Purpose |
448
- |--------|---------|
449
- | `page:portal:layout` | Entire portal shell |
450
- | `section:portal:header` | Header bar |
451
- | `section:portal:footer` | Footer |
452
- | `section:portal:sidebar` | Navigation sidebar |
453
- | `section:portal:user-menu` | User dropdown |
@@ -1,117 +0,0 @@
1
- # Events Package — Standalone Developer Guide
2
-
3
- `@open-mercato/events` provides event-driven communication between modules. Use events for side effects — never direct module-to-module function calls.
4
-
5
- ## Declaring Events
6
-
7
- Every module that emits events MUST declare them in `events.ts`:
8
-
9
- ```typescript
10
- import { createModuleEvents } from '@open-mercato/shared/modules/events'
11
-
12
- const events = [
13
- { id: 'my_mod.item.created', label: 'Item Created', entity: 'item', category: 'crud' },
14
- { id: 'my_mod.item.updated', label: 'Item Updated', entity: 'item', category: 'crud' },
15
- { id: 'my_mod.item.deleted', label: 'Item Deleted', entity: 'item', category: 'crud' },
16
- ] as const
17
-
18
- export const eventsConfig = createModuleEvents({ moduleId: 'my_mod', events })
19
- export const emitMyModEvent = eventsConfig.emit
20
- export default eventsConfig
21
- ```
22
-
23
- MUST use `as const` — provides compile-time safety. Run `yarn generate` after adding.
24
-
25
- ### Event ID Convention
26
-
27
- Format: `module.entity.action` (singular entity, past tense action, dots as separators).
28
-
29
- ## Adding Subscribers
30
-
31
- Create subscriber files in `src/modules/<module>/subscribers/`:
32
-
33
- ```typescript
34
- // subscribers/item-created-notify.ts
35
- export const metadata = {
36
- event: 'my_mod.item.created',
37
- persistent: true, // survives restarts, retried on failure
38
- id: 'item-created-notify',
39
- }
40
-
41
- export default async function handler(payload, ctx) {
42
- // One side effect per subscriber
43
- }
44
- ```
45
-
46
- Run `yarn generate` after adding.
47
-
48
- ## Subscription Types
49
-
50
- | Type | Use for | Behavior |
51
- |------|---------|----------|
52
- | Ephemeral (`persistent: false`) | Real-time UI updates, cache invalidation | In-memory, no retry, lost on restart |
53
- | Persistent (`persistent: true`) | Notifications, indexing, audit logging | Queue-backed, retried on failure |
54
-
55
- Persistent subscribers MUST be idempotent — they may be retried.
56
-
57
- ## DOM Event Bridge (Browser Events via SSE)
58
-
59
- Stream server-side events to the browser for real-time UI updates.
60
-
61
- ### Enable on Events
62
-
63
- Add `clientBroadcast: true` to event declarations:
64
-
65
- ```typescript
66
- const events = [
67
- { id: 'my_mod.item.created', label: 'Created', category: 'crud', clientBroadcast: true },
68
- ] as const
69
- ```
70
-
71
- ### Consume in Components
72
-
73
- ```typescript
74
- import { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'
75
-
76
- // Wildcard: all events from a module
77
- useAppEvent('my_mod.*', (event) => {
78
- // event.id, event.payload — refresh data, show notification
79
- }, [])
80
-
81
- // Exact match
82
- useAppEvent('my_mod.item.created', (event) => {
83
- reloadItems()
84
- }, [reloadItems])
85
- ```
86
-
87
- ### Track Long-Running Operations
88
-
89
- ```typescript
90
- import { useOperationProgress } from '@open-mercato/ui/backend/injection/useOperationProgress'
91
-
92
- const progress = useOperationProgress('my_mod.import.*')
93
- // progress.status: 'idle' | 'running' | 'completed' | 'failed'
94
- // progress.progress: 0-100
95
- ```
96
-
97
- ### SSE Audience Filtering
98
-
99
- Events are server-filtered before SSE delivery by:
100
- - `tenantId` (must match)
101
- - `organizationId` / `organizationIds` (must match selected org)
102
- - `recipientUserId` / `recipientUserIds` (must include user)
103
- - `recipientRoleId` / `recipientRoleIds` (must intersect roles)
104
-
105
- Missing `tenantId` in payload = no delivery.
106
-
107
- ## Queue Integration
108
-
109
- | Queue strategy | Ephemeral | Persistent |
110
- |----------------|-----------|------------|
111
- | `local` (dev) | In-process | `.mercato/queue/` filesystem |
112
- | `async` (prod) | In-process | BullMQ (Redis-backed) |
113
-
114
- For production, start the event worker:
115
- ```bash
116
- yarn mercato events worker event-processing --concurrency=5
117
- ```