@open-mercato/cli 0.4.7-main-1768da2e43 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/build.mjs +8 -1
  2. package/dist/agentic/claude-code/CLAUDE.md.template +1 -0
  3. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +121 -0
  4. package/dist/agentic/claude-code/mcp.json.example +13 -0
  5. package/dist/agentic/claude-code/settings.json +16 -0
  6. package/dist/agentic/codex/enforcement-rules.md +20 -0
  7. package/dist/agentic/codex/mcp.json.example +12 -0
  8. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +69 -0
  9. package/dist/agentic/cursor/hooks.json +10 -0
  10. package/dist/agentic/cursor/mcp.json.example +13 -0
  11. package/dist/agentic/cursor/rules/entity-guard.mdc +29 -0
  12. package/dist/agentic/cursor/rules/generated-guard.mdc +12 -0
  13. package/dist/agentic/cursor/rules/open-mercato.mdc +34 -0
  14. package/dist/agentic/shared/AGENTS.md.template +180 -0
  15. package/dist/agentic/shared/ai/lessons.md +4 -0
  16. package/dist/agentic/shared/ai/skills/backend-ui-design/SKILL.md +197 -0
  17. package/dist/agentic/shared/ai/skills/backend-ui-design/references/ui-components.md +233 -0
  18. package/dist/agentic/shared/ai/skills/code-review/SKILL.md +144 -0
  19. package/dist/agentic/shared/ai/skills/code-review/references/review-checklist.md +77 -0
  20. package/dist/agentic/shared/ai/skills/data-model-design/SKILL.md +512 -0
  21. package/dist/agentic/shared/ai/skills/data-model-design/references/mikro-orm-cheatsheet.md +146 -0
  22. package/dist/agentic/shared/ai/skills/eject-and-customize/SKILL.md +318 -0
  23. package/dist/agentic/shared/ai/skills/integration-builder/SKILL.md +722 -0
  24. package/dist/agentic/shared/ai/skills/integration-builder/references/adapter-contracts.md +762 -0
  25. package/dist/agentic/shared/ai/skills/module-scaffold/SKILL.md +614 -0
  26. package/dist/agentic/shared/ai/skills/module-scaffold/references/naming-conventions.md +61 -0
  27. package/dist/agentic/shared/ai/skills/spec-writing/SKILL.md +76 -0
  28. package/dist/agentic/shared/ai/skills/spec-writing/references/spec-checklist.md +50 -0
  29. package/dist/agentic/shared/ai/skills/spec-writing/references/spec-template.md +86 -0
  30. package/dist/agentic/shared/ai/skills/system-extension/SKILL.md +858 -0
  31. package/dist/agentic/shared/ai/skills/system-extension/references/extension-contracts.md +271 -0
  32. package/dist/agentic/shared/ai/skills/troubleshooter/SKILL.md +421 -0
  33. package/dist/agentic/shared/ai/skills/troubleshooter/references/diagnostic-commands.md +101 -0
  34. package/dist/agentic/shared/ai/specs/README.md +26 -0
  35. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +35 -0
  36. package/dist/lib/agentic-init.js +54 -0
  37. package/dist/lib/agentic-init.js.map +7 -0
  38. package/dist/lib/agentic-setup.js +209 -0
  39. package/dist/lib/agentic-setup.js.map +7 -0
  40. package/dist/lib/generators/module-registry.js +3 -3
  41. package/dist/lib/generators/module-registry.js.map +2 -2
  42. package/dist/lib/testing/integration.js +8 -2
  43. package/dist/lib/testing/integration.js.map +2 -2
  44. package/dist/mercato.js +5 -0
  45. package/dist/mercato.js.map +2 -2
  46. package/package.json +7 -4
  47. package/src/lib/agentic-init.ts +69 -0
  48. package/src/lib/agentic-setup.ts +277 -0
  49. package/src/lib/generators/module-registry.ts +3 -3
  50. package/src/lib/testing/integration.ts +8 -2
  51. package/src/mercato.ts +7 -0
package/build.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as esbuild from 'esbuild'
2
2
  import { glob } from 'glob'
3
- import { readFileSync, writeFileSync, chmodSync, existsSync } from 'node:fs'
3
+ import { readFileSync, writeFileSync, chmodSync, existsSync, cpSync } from 'node:fs'
4
4
  import { dirname, join } from 'node:path'
5
5
  import { fileURLToPath } from 'node:url'
6
6
 
@@ -92,4 +92,11 @@ const binContent = readFileSync(binPath, 'utf-8')
92
92
  writeFileSync(binPath, '#!/usr/bin/env node\n' + binContent)
93
93
  chmodSync(binPath, 0o755)
94
94
 
95
+ // Copy agentic source files from create-app so generators can read them at runtime
96
+ const agenticSrc = join(__dirname, '..', 'create-app', 'agentic')
97
+ if (existsSync(agenticSrc)) {
98
+ cpSync(agenticSrc, join(outdir, 'agentic'), { recursive: true })
99
+ console.log('Copied create-app/agentic/ → dist/agentic/')
100
+ }
101
+
95
102
  console.log('CLI built successfully')
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Claude Code PostToolUse hook: entity-migration-check
3
+ *
4
+ * Fires after Write/Edit/MultiEdit. Detects entity file modifications
5
+ * without an accompanying migration and blocks the agent with a reminder.
6
+ *
7
+ * Three-state logic:
8
+ * 1. Migration file was written → exit 0 (agent is already on it)
9
+ * 2. Not an entity file → exit 0 (not relevant)
10
+ * 3. Entity written, no fresh migration → block with migration instructions
11
+ */
12
+
13
+ import { readdirSync, statSync } from 'node:fs'
14
+ import { join, relative } from 'node:path'
15
+
16
+ const ENTITY_PATTERN = /^src\/modules\/([^/]+)\/entities\/.*\.ts$/
17
+ const MIGRATION_PATTERN = /^src\/modules\/([^/]+)\/migrations\/.*\.ts$/
18
+
19
+ interface PostToolUseInput {
20
+ tool_input?: {
21
+ file_path?: string
22
+ content?: string
23
+ }
24
+ tool_name?: string
25
+ }
26
+
27
+ function getRelativePath(filePath: string): string {
28
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd()
29
+ if (filePath.startsWith('/')) {
30
+ return relative(projectDir, filePath)
31
+ }
32
+ return filePath
33
+ }
34
+
35
+ function hasFreshMigration(moduleId: string): boolean {
36
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd()
37
+ const migrationsDir = join(projectDir, 'src', 'modules', moduleId, 'migrations')
38
+
39
+ try {
40
+ const files = readdirSync(migrationsDir)
41
+ const now = Date.now()
42
+ const thirtySecondsAgo = now - 30_000
43
+
44
+ for (const file of files) {
45
+ if (!file.endsWith('.ts')) continue
46
+ const stat = statSync(join(migrationsDir, file))
47
+ if (stat.mtimeMs > thirtySecondsAgo) {
48
+ return true
49
+ }
50
+ }
51
+ } catch {
52
+ // migrations directory doesn't exist yet — that's fine
53
+ }
54
+
55
+ return false
56
+ }
57
+
58
+ async function main(): Promise<void> {
59
+ let input = ''
60
+ for await (const chunk of process.stdin) {
61
+ input += chunk
62
+ }
63
+
64
+ if (!input.trim()) {
65
+ process.exit(0)
66
+ }
67
+
68
+ let data: PostToolUseInput
69
+ try {
70
+ data = JSON.parse(input)
71
+ } catch {
72
+ process.exit(0)
73
+ }
74
+
75
+ const filePath = data.tool_input?.file_path
76
+ if (!filePath) {
77
+ process.exit(0)
78
+ }
79
+
80
+ const rel = getRelativePath(filePath)
81
+
82
+ // State 1: Migration file was written → agent is already handling it
83
+ if (MIGRATION_PATTERN.test(rel)) {
84
+ process.exit(0)
85
+ }
86
+
87
+ // State 2: Not an entity file → not relevant
88
+ const entityMatch = rel.match(ENTITY_PATTERN)
89
+ if (!entityMatch) {
90
+ process.exit(0)
91
+ }
92
+
93
+ const moduleId = entityMatch[1]
94
+
95
+ // State 3: Entity written — check for fresh migration
96
+ if (hasFreshMigration(moduleId)) {
97
+ process.exit(0)
98
+ }
99
+
100
+ // Block: entity modified without migration
101
+ const result = {
102
+ decision: 'block',
103
+ reason: [
104
+ `Entity file modified: ${rel}`,
105
+ '',
106
+ `Affected module: ${moduleId}`,
107
+ '',
108
+ 'A database migration is likely needed. Ask the user whether to run:',
109
+ ' yarn db:generate',
110
+ '',
111
+ 'Then regenerate registries:',
112
+ ' yarn generate',
113
+ '',
114
+ 'Do NOT run migrations automatically — always confirm with the user first.',
115
+ ].join('\n'),
116
+ }
117
+
118
+ process.stdout.write(JSON.stringify(result))
119
+ }
120
+
121
+ main()
@@ -0,0 +1,13 @@
1
+ {
2
+ "_comment": "Copy to .mcp.json and set MCP_API_KEY (printed by yarn initialize)",
3
+ "mcpServers": {
4
+ "open-mercato": {
5
+ "command": "yarn",
6
+ "args": ["mercato", "mcp:dev"],
7
+ "env": {
8
+ "APP_URL": "http://localhost:3000",
9
+ "MCP_API_KEY": "${MCP_API_KEY}"
10
+ }
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "matcher": "Write|Edit|MultiEdit",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "npx tsx \"$CLAUDE_PROJECT_DIR/.claude/hooks/entity-migration-check.ts\"",
10
+ "timeout": 15
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,20 @@
1
+ <!-- CODEX_ENFORCEMENT_RULES_START -->
2
+ ## CRITICAL rules — always follow without exception
3
+
4
+ 1. **After editing any entity file** (`src/modules/<id>/entities/*.ts`):
5
+ - STOP immediately before any further action
6
+ - Tell the user: "I modified an entity in module <id>. Should I create a migration?"
7
+ - If yes: run `yarn db:generate`
8
+ - Show the generated migration to the user before applying
9
+ - Ask for confirmation, then run `yarn db:migrate`
10
+ - Run `yarn generate` after migration is applied
11
+
12
+ 2. **After editing `src/modules.ts`**: immediately run `yarn generate`
13
+
14
+ 3. **Never edit `.mercato/generated/*`**: edit the source and run `yarn generate` instead
15
+
16
+ 4. **Before significant features**: check `.ai/specs/` for an existing spec.
17
+ If none exists, ask the user whether to create one first.
18
+
19
+ ---
20
+ <!-- CODEX_ENFORCEMENT_RULES_END -->
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "open-mercato": {
4
+ "command": "yarn",
5
+ "args": ["mercato", "mcp:dev"],
6
+ "env": {
7
+ "APP_URL": "http://localhost:3000",
8
+ "MCP_API_KEY": "${MCP_API_KEY}"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Cursor afterFileEdit hook: entity-migration-check
3
+ *
4
+ * Detects entity file modifications without an accompanying migration
5
+ * and exits non-zero to block the agent with a reminder.
6
+ *
7
+ * Three-state logic:
8
+ * 1. Migration file was written → exit 0 (agent is already on it)
9
+ * 2. Not an entity file → exit 0 (not relevant)
10
+ * 3. Entity written, no fresh migration → exit 1 with warning
11
+ */
12
+
13
+ import { readdirSync, statSync } from 'node:fs'
14
+ import { join, resolve } from 'node:path'
15
+
16
+ const ENTITY_RE = /^src\/modules\/([^/]+)\/entities\/.*\.ts$/
17
+ const MIGRATION_RE = /^src\/modules\/([^/]+)\/migrations\/.*\.ts$/
18
+
19
+ function hasFreshMigration(projectDir, moduleId) {
20
+ const migrationsDir = join(projectDir, 'src', 'modules', moduleId, 'migrations')
21
+ try {
22
+ const files = readdirSync(migrationsDir)
23
+ const cutoff = Date.now() - 30_000
24
+ for (const file of files) {
25
+ if (!file.endsWith('.ts')) continue
26
+ const stat = statSync(join(migrationsDir, file))
27
+ if (stat.mtimeMs > cutoff) return true
28
+ }
29
+ } catch {
30
+ // migrations directory doesn't exist yet
31
+ }
32
+ return false
33
+ }
34
+
35
+ const filePath = process.argv[2]
36
+ if (!filePath) process.exit(0)
37
+
38
+ const projectDir = resolve('.')
39
+ const rel = filePath.startsWith('/') ? filePath.slice(projectDir.length + 1) : filePath
40
+
41
+ // State 1: Migration file written
42
+ if (MIGRATION_RE.test(rel)) process.exit(0)
43
+
44
+ // State 2: Not an entity file
45
+ const match = rel.match(ENTITY_RE)
46
+ if (!match) process.exit(0)
47
+
48
+ const moduleId = match[1]
49
+
50
+ // State 3: Entity file without fresh migration
51
+ if (hasFreshMigration(projectDir, moduleId)) process.exit(0)
52
+
53
+ process.stderr.write(
54
+ [
55
+ `Entity file modified: ${rel}`,
56
+ '',
57
+ `Affected module: ${moduleId}`,
58
+ '',
59
+ 'A database migration is likely needed. Ask the user whether to run:',
60
+ ' yarn db:generate',
61
+ '',
62
+ 'Then regenerate registries:',
63
+ ' yarn generate',
64
+ '',
65
+ 'Do NOT run migrations automatically — always confirm with the user first.',
66
+ '',
67
+ ].join('\n'),
68
+ )
69
+ process.exit(1)
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ "afterFileEdit": {
5
+ "command": "node",
6
+ "args": [".cursor/hooks/entity-migration-check.mjs"],
7
+ "description": "Check if entity edit requires a database migration"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "_comment": "Copy to .cursor/mcp.json and set MCP_API_KEY (printed by yarn initialize)",
3
+ "mcpServers": {
4
+ "open-mercato": {
5
+ "command": "yarn",
6
+ "args": ["mercato", "mcp:dev"],
7
+ "env": {
8
+ "APP_URL": "http://localhost:3000",
9
+ "MCP_API_KEY": "${MCP_API_KEY}"
10
+ }
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,29 @@
1
+ ---
2
+ description: Entity modification guard — triggers migration workflow
3
+ globs:
4
+ - src/modules/*/entities/**/*.ts
5
+ alwaysApply: false
6
+ ---
7
+
8
+ # Entity Modification — Migration Required
9
+
10
+ You are editing an entity file. After saving, you MUST:
11
+
12
+ 1. **Create a migration** — ask the user before running:
13
+ ```
14
+ yarn db:generate
15
+ ```
16
+
17
+ 2. **Show the migration** to the user and wait for confirmation before applying
18
+
19
+ 3. **Apply the migration** (only after user confirms):
20
+ ```
21
+ yarn db:migrate
22
+ ```
23
+
24
+ 4. **Regenerate registries**:
25
+ ```
26
+ yarn generate
27
+ ```
28
+
29
+ Do NOT skip any step. Do NOT run migrations without user confirmation.
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: Prevent editing auto-generated files
3
+ globs:
4
+ - .mercato/generated/**
5
+ alwaysApply: false
6
+ ---
7
+
8
+ # NEVER Edit Generated Files
9
+
10
+ This file is auto-generated by `yarn generate`. Any manual edits will be overwritten.
11
+
12
+ To change generated output, edit the source modules in `src/modules/` and run `yarn generate`.
@@ -0,0 +1,34 @@
1
+ ---
2
+ description: Core Open Mercato conventions for standalone app development
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # {{PROJECT_NAME}} — Project Rules
7
+
8
+ ## What this project is
9
+
10
+ A standalone Open Mercato application. Build domain features ON TOP of the framework.
11
+ To customise a core module beyond the extension system: `yarn mercato eject <module>`.
12
+ Never edit node_modules/@open-mercato/* directly.
13
+
14
+ ## Task routing
15
+
16
+ Read AGENTS.md before starting any task — it maps task types to the minimum files to load.
17
+
18
+ ## Module system
19
+
20
+ Modules in `src/modules/<id>/`. Register with `from: '@app'` in `src/modules.ts`.
21
+ Run `yarn generate` after any module or entity change. Never edit `.mercato/generated/*`.
22
+
23
+ ## Entity and migration rule
24
+
25
+ After editing any entity in `src/modules/<id>/entities/`:
26
+ 1. Create migration: `yarn db:generate`
27
+ 2. Show migration to user, confirm before applying
28
+ 3. Apply: `yarn db:migrate`
29
+ 4. Regenerate: `yarn generate`
30
+
31
+ ## Spec-driven development
32
+
33
+ Check `.ai/specs/` before any significant feature. If no spec exists, ask the user
34
+ whether to create one first.
@@ -0,0 +1,180 @@
1
+ # Agent Context Routing — {{PROJECT_NAME}}
2
+
3
+ Read this file before any task. Load ONLY the files listed for your task type.
4
+ Do NOT load the entire src/ tree — Open Mercato apps can have many modules.
5
+
6
+ ## What This Project Is
7
+
8
+ A standalone Open Mercato application built ON TOP of the framework.
9
+ The framework lives in `node_modules/@open-mercato/*`. Never edit `node_modules` directly.
10
+ To customise a built-in module beyond extensions, eject with `yarn mercato eject <module>`.
11
+
12
+ ## Task → Context Map
13
+
14
+ Match your task, then load the listed file(s) BEFORE writing code. A task may match multiple rows.
15
+
16
+ ### Module Development
17
+
18
+ | Task | Load |
19
+ |---|---|
20
+ | Scaffold a new module from scratch | `.ai/skills/module-scaffold/SKILL.md` |
21
+ | Design entities and relationships | `.ai/skills/data-model-design/SKILL.md` |
22
+ | Build backend UI (forms, tables, pages) | `.ai/skills/backend-ui-design/SKILL.md` |
23
+ | Build an integration provider | `.ai/skills/integration-builder/SKILL.md` |
24
+
25
+ ### Extending Core Modules (UMES)
26
+
27
+ | Task | Load |
28
+ |---|---|
29
+ | Extend a core module (add fields, columns, menus, interceptors, enrichers) | `.ai/skills/system-extension/SKILL.md` |
30
+ | Eject and customize a core module | `.ai/skills/eject-and-customize/SKILL.md` |
31
+ | Add a response enricher to another module's API | `.ai/guides/core.md` → Response Enrichers |
32
+ | Add an API interceptor (before/after hooks) | `.ai/guides/core.md` → API Interceptors |
33
+ | Inject widgets into forms/tables/menus | `.ai/guides/core.md` → Widget Injection |
34
+ | Replace or wrap a UI component | `.ai/guides/core.md` → Component Replacement |
35
+
36
+ ### Framework Feature Usage
37
+
38
+ | Task | Load |
39
+ |---|---|
40
+ | Add/modify an entity, create migration | `.ai/guides/core.md` → Module Files, then `yarn db:generate` |
41
+ | Add a REST API endpoint | `.ai/guides/core.md` → API Routes |
42
+ | Add a backend page | `.ai/guides/ui.md` → CrudForm / DataTable |
43
+ | Add event subscribers or emit events | `.ai/guides/events.md` |
44
+ | Add real-time browser updates (SSE) | `.ai/guides/events.md` → DOM Event Bridge |
45
+ | Add search to a module | `.ai/guides/search.md` |
46
+ | Add caching | `.ai/guides/cache.md` |
47
+ | Add background workers | `.ai/guides/queue.md` |
48
+ | Use i18n (translations) | `.ai/guides/shared.md` → i18n |
49
+ | Use encrypted queries | `.ai/guides/shared.md` → Encryption |
50
+ | Use apiCall / UI components | `.ai/guides/ui.md` |
51
+ | Add permissions (RBAC) | `.ai/guides/core.md` → Access Control |
52
+ | Add notifications | `.ai/guides/core.md` → Notifications |
53
+ | Add custom fields | `.ai/guides/core.md` → Custom Fields |
54
+
55
+ ### Quality & Process
56
+
57
+ | Task | Load |
58
+ |---|---|
59
+ | Debug / fix errors | `.ai/skills/troubleshooter/SKILL.md` |
60
+ | Review code changes | `.ai/skills/code-review/SKILL.md` |
61
+ | Write a spec | `.ai/skills/spec-writing/SKILL.md`, `.ai/specs/SPEC-000-template.md` |
62
+
63
+ ## Module Anatomy
64
+
65
+ Each module in `src/modules/<id>/` is self-contained and auto-discovered:
66
+
67
+ ```
68
+ src/modules/<id>/
69
+ ├── index.ts # Module metadata
70
+ ├── data/
71
+ │ ├── entities.ts # MikroORM entity classes
72
+ │ ├── validators.ts # Zod validation schemas
73
+ │ ├── extensions.ts # Cross-module entity links
74
+ │ └── enrichers.ts # Response enrichers
75
+ ├── api/
76
+ │ ├── <resource>/route.ts # REST handlers (auto-discovered by method)
77
+ │ └── interceptors.ts # API route interception hooks
78
+ ├── backend/ # Admin UI pages (auto-discovered)
79
+ │ └── page.tsx # → /backend/<module>
80
+ ├── frontend/ # Public pages (auto-discovered)
81
+ ├── subscribers/ # Event handlers (export metadata + default handler)
82
+ ├── workers/ # Background jobs (export metadata + default handler)
83
+ ├── widgets/
84
+ │ ├── injection/ # UI widgets injected into other modules
85
+ │ ├── injection-table.ts # Widget-to-slot mappings
86
+ │ └── components.ts # Component replacement/wrapper definitions
87
+ ├── di.ts # Awilix DI registrations
88
+ ├── acl.ts # Permission features
89
+ ├── setup.ts # Tenant init, role features, seed data
90
+ ├── events.ts # Typed event declarations
91
+ ├── search.ts # Search indexing configuration
92
+ ├── ce.ts # Custom entities / custom field sets
93
+ ├── translations.ts # Translatable fields per entity
94
+ ├── notifications.ts # Notification type definitions
95
+ └── notifications.client.ts # Client-side notification renderers
96
+ ```
97
+
98
+ Register in `src/modules.ts`: `{ id: '<id>', from: '@app' }`
99
+
100
+ ## Critical Conventions
101
+
102
+ - After any module/entity change: `yarn generate`
103
+ - After any entity edit: `yarn db:generate` (never hand-write migrations)
104
+ - NEVER edit `.mercato/generated/*` — auto-generated
105
+ - NEVER edit `node_modules/@open-mercato/*` — eject instead
106
+ - Custom modules use `from: '@app'` in `src/modules.ts`
107
+ - Confirm migrations with user before `yarn db:migrate`
108
+
109
+ ## Naming Conventions
110
+
111
+ - Module IDs: plural, snake_case (`order_items`)
112
+ - Event IDs: `module.entity.action` (singular entity, past tense: `sales.order.created`)
113
+ - DB tables: plural, snake_case with module prefix (`catalog_products`)
114
+ - DB columns: snake_case (`created_at`, `organization_id`)
115
+ - JS/TS identifiers: camelCase
116
+ - Feature IDs: `<module>.<action>` (`my_module.view`, `my_module.create`)
117
+ - UUID primary keys, explicit foreign keys, junction tables for M2M
118
+
119
+ ## Key Imports Quick Reference
120
+
121
+ ```typescript
122
+ // Translations
123
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
124
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
125
+
126
+ // API calls (MUST use — never raw fetch)
127
+ import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
128
+
129
+ // CRUD forms
130
+ import { CrudForm, createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/crud'
131
+ import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
132
+
133
+ // UI components (MUST use — never raw <button>)
134
+ import { Button } from '@open-mercato/ui/primitives/button'
135
+ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
136
+ import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
137
+ import { FormHeader, FormFooter } from '@open-mercato/ui/backend/forms'
138
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
139
+
140
+ // Encrypted queries (MUST use instead of em.find)
141
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
142
+
143
+ // Events
144
+ import { createModuleEvents } from '@open-mercato/shared/modules/events'
145
+
146
+ // Widget injection
147
+ import { InjectionPosition } from '@open-mercato/shared/modules/widgets/injection-position'
148
+
149
+ // Types
150
+ import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
151
+ import type { ResponseEnricher } from '@open-mercato/shared/lib/crud/response-enricher'
152
+ import type { ApiInterceptor } from '@open-mercato/shared/lib/crud/api-interceptor'
153
+ ```
154
+
155
+ ## Key Commands
156
+
157
+ | Command | Purpose |
158
+ |---|---|
159
+ | `yarn dev` | Start dev server |
160
+ | `yarn generate` | Regenerate `.mercato/generated/` |
161
+ | `yarn db:generate` | Create migration for entity changes |
162
+ | `yarn db:migrate` | Apply pending migrations |
163
+ | `yarn initialize` | Bootstrap DB + first admin account |
164
+ | `yarn build` | Build for production |
165
+ | `yarn mercato eject <module>` | Copy a core module into `src/modules/` |
166
+
167
+ ## Architecture Rules
168
+
169
+ - NO direct ORM relationships between modules — use foreign key IDs
170
+ - Always filter by `organization_id` for tenant-scoped entities
171
+ - Validate all inputs with Zod; derive types via `z.infer`
172
+ - Use DI (Awilix) for services; avoid `new`-ing directly
173
+ - No `any` types — use Zod schemas with `z.infer`, narrow with runtime checks
174
+ - Every dialog: `Cmd/Ctrl+Enter` submit, `Escape` cancel
175
+ - Keep `pageSize` at or below 100
176
+ - Every API route MUST export `openApi`
177
+
178
+ ## Stack
179
+
180
+ Next.js App Router, TypeScript, MikroORM, Awilix DI, Zod
@@ -0,0 +1,4 @@
1
+ # Lessons Learned
2
+
3
+ Record patterns, mistakes, and insights discovered during development.
4
+ AI agents will update this file as they work on the codebase.