@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.
- package/.turbo/turbo-build.log +4 -4
- package/build.mjs +7 -24
- package/dist/agentic/claude-code/hooks/entity-migration-check.ts +88 -32
- package/dist/agentic/codex/enforcement-rules.md +16 -2
- package/dist/agentic/cursor/hooks/entity-migration-check.mjs +50 -19
- package/dist/agentic/cursor/rules/entity-guard.mdc +21 -9
- package/dist/agentic/cursor/rules/generated-guard.mdc +5 -3
- package/dist/agentic/cursor/rules/open-mercato.mdc +29 -3
- package/dist/agentic/guides/cache.md +45 -0
- package/dist/agentic/guides/core.md +453 -0
- package/dist/agentic/guides/events.md +117 -0
- package/dist/agentic/guides/module-facts.json +3833 -115419
- package/dist/agentic/guides/module-system.md +127 -12
- package/dist/agentic/guides/modules/ai_assistant.md +28 -227
- package/dist/agentic/guides/modules/api_docs.md +2 -58
- package/dist/agentic/guides/modules/api_keys.md +7 -92
- package/dist/agentic/guides/modules/attachments.md +14 -119
- package/dist/agentic/guides/modules/audit_logs.md +10 -131
- package/dist/agentic/guides/modules/auth.md +33 -276
- package/dist/agentic/guides/modules/business_rules.md +14 -149
- package/dist/agentic/guides/modules/catalog.md +43 -475
- package/dist/agentic/guides/modules/channel_gmail.md +3 -82
- package/dist/agentic/guides/modules/channel_imap.md +3 -82
- package/dist/agentic/guides/modules/checkout.md +27 -299
- package/dist/agentic/guides/modules/communication_channels.md +33 -315
- package/dist/agentic/guides/modules/configs.md +8 -104
- package/dist/agentic/guides/modules/content.md +2 -57
- package/dist/agentic/guides/modules/currencies.md +17 -152
- package/dist/agentic/guides/modules/customer_accounts.md +45 -387
- package/dist/agentic/guides/modules/customers.md +85 -746
- package/dist/agentic/guides/modules/dashboards.md +10 -155
- package/dist/agentic/guides/modules/data_sync.md +16 -149
- package/dist/agentic/guides/modules/dictionaries.md +12 -107
- package/dist/agentic/guides/modules/directory.md +15 -148
- package/dist/agentic/guides/modules/entities.md +13 -145
- package/dist/agentic/guides/modules/events.md +2 -59
- package/dist/agentic/guides/modules/feature_toggles.md +9 -119
- package/dist/agentic/guides/modules/gateway_stripe.md +4 -92
- package/dist/agentic/guides/modules/generators.md +2 -45
- package/dist/agentic/guides/modules/inbox_ops.md +27 -207
- package/dist/agentic/guides/modules/integrations.md +16 -146
- package/dist/agentic/guides/modules/messages.md +24 -223
- package/dist/agentic/guides/modules/notifications.md +8 -96
- package/dist/agentic/guides/modules/onboarding.md +6 -77
- package/dist/agentic/guides/modules/payment_gateways.md +21 -164
- package/dist/agentic/guides/modules/perspectives.md +7 -77
- package/dist/agentic/guides/modules/planner.md +18 -136
- package/dist/agentic/guides/modules/portal.md +2 -93
- package/dist/agentic/guides/modules/progress.md +14 -104
- package/dist/agentic/guides/modules/query_index.md +15 -154
- package/dist/agentic/guides/modules/record_locks.md +21 -206
- package/dist/agentic/guides/modules/resources.md +30 -197
- package/dist/agentic/guides/modules/sales.md +84 -678
- package/dist/agentic/guides/modules/scheduler.md +14 -133
- package/dist/agentic/guides/modules/search.md +6 -170
- package/dist/agentic/guides/modules/security.md +35 -294
- package/dist/agentic/guides/modules/shipping_carriers.md +18 -134
- package/dist/agentic/guides/modules/sso.md +26 -151
- package/dist/agentic/guides/modules/staff.md +57 -369
- package/dist/agentic/guides/modules/storage_s3.md +5 -95
- package/dist/agentic/guides/modules/sync_akeneo.md +3 -85
- package/dist/agentic/guides/modules/sync_excel.md +6 -75
- package/dist/agentic/guides/modules/system_status_overlays.md +2 -60
- package/dist/agentic/guides/modules/translations.md +10 -104
- package/dist/agentic/guides/modules/webhooks.md +24 -203
- package/dist/agentic/guides/modules/wms.md +46 -441
- package/dist/agentic/guides/modules/workflows.md +43 -302
- package/dist/agentic/guides/queue.md +53 -0
- package/dist/agentic/guides/search.md +115 -0
- package/dist/agentic/guides/shared.md +108 -0
- package/dist/agentic/guides/ui.md +347 -0
- package/dist/agentic/shared/AGENTS.md.template +311 -124
- package/dist/agentic/shared/ai/agentic.config.json +0 -2
- package/dist/agentic/shared/ai/lessons.md +3 -26
- package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +27 -0
- package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +27 -0
- package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +0 -4
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +241 -20
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +260 -0
- package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +601 -16
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +146 -0
- package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +310 -14
- package/dist/agentic/shared/ai/skills/om-help/SKILL.md +97 -15
- package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +84 -0
- package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +193 -0
- package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +152 -33
- package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +708 -24
- package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +34 -0
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +762 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +820 -20
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +50 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +97 -0
- package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +843 -19
- package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +271 -0
- package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +63 -15
- package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +467 -16
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +101 -0
- package/dist/agentic/shared/ai/skills/tiers.json +21 -143
- package/dist/agentic/shared/ai/skills/tiers.schema.json +40 -52
- package/dist/agentic/shared/ai/specs/README.md +3 -4
- package/dist/agentic/shared/ai/specs/SPEC-000-template.md +10 -226
- package/dist/agentic/shared/ai/trackers/github.md +4 -108
- package/dist/agentic/shared/scripts/install-skills.sh +694 -4
- package/dist/bin.js +1 -13
- package/dist/bin.js.map +2 -2
- package/dist/lib/__integration__/TC-INT-008.spec.js +8 -88
- package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
- package/dist/lib/agentic-init.js +3 -13
- package/dist/lib/agentic-init.js.map +2 -2
- package/dist/lib/agentic-setup.js +186 -418
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/generators/extensions/injection-widgets.js +0 -20
- package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
- package/dist/lib/generators/module-facts-discovery.js +7 -41
- package/dist/lib/generators/module-facts-discovery.js.map +2 -2
- package/dist/lib/generators/module-facts.js +45 -1387
- package/dist/lib/generators/module-facts.js.map +3 -3
- package/dist/lib/generators/module-registry.js +5 -11
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/resolver.js +0 -2
- package/dist/lib/resolver.js.map +2 -2
- package/dist/lib/testing/integration.js +0 -5
- package/dist/lib/testing/integration.js.map +2 -2
- package/dist/mercato.js +1 -23
- package/dist/mercato.js.map +2 -2
- package/package.json +11 -13
- package/src/bin.ts +1 -33
- package/src/lib/__integration__/TC-INT-008.spec.ts +9 -107
- package/src/lib/__tests__/agentic-init.test.ts +5 -41
- package/src/lib/agentic-init.ts +3 -15
- package/src/lib/agentic-setup.ts +224 -530
- package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +2 -88
- package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +0 -1
- package/src/lib/generators/__tests__/module-facts.discovery.test.ts +10 -54
- package/src/lib/generators/__tests__/module-subset.test.ts +0 -18
- package/src/lib/generators/extensions/injection-widgets.ts +0 -51
- package/src/lib/generators/module-facts-discovery.ts +7 -58
- package/src/lib/generators/module-facts.ts +46 -1851
- package/src/lib/generators/module-registry.ts +6 -16
- package/src/lib/resolver.ts +0 -3
- package/src/lib/testing/__tests__/integration.test.ts +0 -4
- package/src/lib/testing/integration.ts +0 -5
- package/src/mercato.ts +1 -26
- package/dist/agentic/guides/ai-workflows.md +0 -76
- package/dist/agentic/guides/architecture.md +0 -107
- package/dist/agentic/guides/backend-ui.md +0 -80
- package/dist/agentic/guides/contracts.md +0 -92
- package/dist/agentic/guides/extensions.md +0 -121
- package/dist/agentic/guides/framework-extension-points.md +0 -36
- package/dist/agentic/guides/integrations.md +0 -81
- package/dist/agentic/guides/modules/design_system.md +0 -102
- package/dist/agentic/guides/spec-delivery.md +0 -22
- package/dist/agentic/guides/testing-debugging.md +0 -80
- package/dist/agentic/guides/upstream/AGENTS.md +0 -298
- package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +0 -316
- package/dist/agentic/guides/upstream/manifest.json +0 -8
- package/dist/agentic/shared/ai/harness/README.md +0 -50
- package/dist/agentic/shared/ai/harness/RELEASE.md +0 -57
- package/dist/agentic/shared/ai/harness/cases.json +0 -13680
- package/dist/agentic/shared/ai/harness/cases.schema.json +0 -133
- package/dist/agentic/shared/ai/harness/fixtures/index.json +0 -49
- package/dist/agentic/shared/ai/harness/fixtures/seeds.json +0 -162
- package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +0 -21
- package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +0 -96
- package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +0 -172
- package/dist/agentic/shared/ai/harness/release-matrix.json +0 -158
- package/dist/agentic/shared/ai/harness/release-result.schema.json +0 -219
- package/dist/agentic/shared/ai/harness/result.schema.json +0 -76
- package/dist/agentic/shared/ai/harness/routing-response.schema.json +0 -12
- package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +0 -73
- package/dist/agentic/shared/ai/harness/validators.json +0 -78
- package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +0 -2121
- package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +0 -1096
- package/dist/agentic/shared/ai/lessons/_template.md +0 -16
- package/dist/agentic/shared/ai/review-checklist.md +0 -36
- package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +0 -21
- package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +0 -98
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +0 -16
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +0 -53
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +0 -13
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +0 -11
- package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +0 -30
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +0 -12
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +0 -13
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +0 -14
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +0 -28
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +0 -12
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +0 -22
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +0 -12
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +0 -19
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +0 -19
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +0 -13
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +0 -14
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +0 -10
- package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +0 -17
- package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +0 -29
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +0 -100
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +0 -16
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +0 -17
- package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +0 -25
- package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +0 -11
- package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +0 -13
- package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +0 -18
- package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +0 -20
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +0 -33
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +0 -40
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +0 -27
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +0 -24
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +0 -16
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +0 -14
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +0 -15
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +0 -12
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +0 -34
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +0 -4
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +0 -11
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +0 -31
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +0 -39
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +0 -43
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +0 -11
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +0 -19
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +0 -172
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +0 -12
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +0 -83
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +0 -21
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +0 -11
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +0 -22
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +0 -13
- package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +0 -41
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +0 -19
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +0 -44
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +0 -39
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +0 -20
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +0 -57
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +0 -11
- package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +0 -486
- package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +0 -18
- package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +0 -27
- package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +0 -13
- package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +0 -41
- package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +0 -11
- package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +0 -15
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +0 -16
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +0 -14
- package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +0 -187
- package/dist/agentic/shared/scripts/check-lessons.mjs +0 -178
- package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +0 -2958
- package/dist/agentic/shared/scripts/execution-sandbox.mjs +0 -329
- package/dist/agentic/shared/scripts/framework-context.mjs +0 -888
- package/dist/agentic/shared/scripts/install-skills.mjs +0 -1029
- package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +0 -222
- package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +0 -1774
- package/dist/lib/generators/module-extension-facts.js +0 -1840
- package/dist/lib/generators/module-extension-facts.js.map +0 -7
- package/dist/lib/generators/module-fact-sources.js +0 -70
- package/dist/lib/generators/module-fact-sources.js.map +0 -7
- package/dist/lib/generators/module-override-targets.js +0 -688
- package/dist/lib/generators/module-override-targets.js.map +0 -7
- package/dist/lib/load-env.js +0 -24
- package/dist/lib/load-env.js.map +0 -7
- package/dist/lib/telemetry-init.js +0 -358
- package/dist/lib/telemetry-init.js.map +0 -7
- package/src/lib/__tests__/agentic-setup.ownership.test.ts +0 -386
- package/src/lib/__tests__/injection-widget-id-validation.test.ts +0 -184
- package/src/lib/__tests__/load-env.test.ts +0 -65
- package/src/lib/__tests__/telemetry-init.test.ts +0 -298
- package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +0 -92
- package/src/lib/generators/__tests__/module-extension-activations.test.ts +0 -762
- package/src/lib/generators/__tests__/module-extension-facts.test.ts +0 -404
- package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +0 -108
- package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +0 -561
- package/src/lib/generators/__tests__/module-facts.source-links.test.ts +0 -119
- package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +0 -149
- package/src/lib/generators/__tests__/module-override-targets.test.ts +0 -304
- package/src/lib/generators/module-extension-facts.ts +0 -2365
- package/src/lib/generators/module-fact-sources.ts +0 -108
- package/src/lib/generators/module-override-targets.ts +0 -863
- package/src/lib/load-env.ts +0 -30
- package/src/lib/telemetry-init.ts +0 -461
|
@@ -0,0 +1,453 @@
|
|
|
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 |
|
|
@@ -0,0 +1,117 @@
|
|
|
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
|
+
```
|