@open-mercato/cli 0.5.1-develop.3043.1a796c3920 → 0.6.0

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/AGENTS.md CHANGED
@@ -40,6 +40,13 @@ yarn generate # Run all generators
40
40
 
41
41
  `yarn generate` now performs a best-effort post-step structural cache purge by invoking `yarn mercato configs cache structural --all-tenants` when the generated app exposes the `configs` cache CLI. This post-step must never break generation; unavailable cache tooling must be treated as a skip.
42
42
 
43
+ The structural cache purge does two things:
44
+
45
+ 1. Deletes Redis cache keys matching `nav:*` so navigation/sidebar caches are rebuilt next render.
46
+ 2. Touches every `*.generated.ts` and `*.generated.checksum` file in the current app's `.mercato/generated/` directory by rewriting them with identical bytes. This advances mtime without changing content, which forces Turbopack's filesystem watcher to invalidate the import graph and recompile leaf files that imported a barrel. Without this, Turbopack can keep serving a cached compile error against a since-fixed module file until the dev server is restarted.
47
+
48
+ The dev escape hatch is `yarn dev:reset`, which clears `.next/cache/turbopack` and `.next/cache/webpack` for the rare case where Turbopack's internal cache stays stuck after a structural purge.
49
+
43
50
  ## Database Migrations
44
51
 
45
52
  Module-scoped migrations using MikroORM:
@@ -1,6 +1,6 @@
1
1
  # Agent Context Routing — {{PROJECT_NAME}}
2
2
 
3
- **MANDATORY CONTEXT LOADING** — see Critical Rule #5 below.
3
+ **MANDATORY CONTEXT LOADING** — see the "BEFORE writing ANY code" Critical Rule below.
4
4
  Before writing code, find your task below and `Read` the listed files.
5
5
  Do NOT load the entire src/ tree — Open Mercato apps can have many modules.
6
6
 
@@ -51,7 +51,8 @@ step, you WILL produce incorrect imports and miss required patterns.
51
51
  | Add caching | `.ai/guides/cache.md` |
52
52
  | Add background workers | `.ai/guides/queue.md` |
53
53
  | Use i18n (translations) | `.ai/guides/shared.md` → i18n |
54
- | Use encrypted queries | `.ai/guides/shared.md` → Encryption |
54
+ | Use encrypted queries (read sensitive columns that already have an encryption map; for authoring a NEW sensitive column see the row below first) | `.ai/guides/shared.md` → Encryption |
55
+ | **Encrypt sensitive / GDPR-relevant fields** ("we need this column encrypted", "store this securely", "this is PII", "GDPR", "encryption at rest", addresses, contact info, free-text notes about people, integration credentials, secrets) — declare them in the framework's encryption-maps mechanism, never hand-rolled AES/KMS | `.ai/skills/data-model-design/SKILL.md` → Sensitive Data and Encryption Maps, then `.ai/skills/module-scaffold/SKILL.md` → Encryption maps. Reference: <https://docs.open-mercato.dev/user-guide/encryption> |
55
56
  | Use apiCall / UI components | `.ai/guides/ui.md` |
56
57
  | Add permissions (RBAC) | `.ai/guides/core.md` → Access Control |
57
58
  | Add notifications | `.ai/guides/core.md` → Notifications |
@@ -90,6 +91,8 @@ These guides ship automatically when the corresponding module is installed.
90
91
  |---|---|
91
92
  | Delegate an arbitrary task end-to-end as a PR | `.ai/skills/auto-create-pr/SKILL.md` |
92
93
  | Resume an in-progress agent PR | `.ai/skills/auto-continue-pr/SKILL.md` |
94
+ | Run a long multi-step spec implementation with resumable checkpoints | `.ai/skills/auto-create-pr-loop/SKILL.md` |
95
+ | Resume a checkpointed PR started by `auto-create-pr-loop` | `.ai/skills/auto-continue-pr-loop/SKILL.md` |
93
96
  | Automated code review on a PR (with optional autofix) | `.ai/skills/auto-review-pr/SKILL.md` |
94
97
  | Fix a GitHub issue by number and open a PR | `.ai/skills/auto-fix-github/SKILL.md` |
95
98
  | Propose disabling unused built-in modules after the user adds a new module (classic-mode slimdown) | `.ai/skills/trim-unused-modules/SKILL.md` |
@@ -128,11 +131,35 @@ src/modules/<id>/
128
131
  ├── ce.ts # Custom entities / custom field sets
129
132
  ├── translations.ts # Translatable fields per entity
130
133
  ├── notifications.ts # Notification type definitions
131
- └── notifications.client.ts # Client-side notification renderers
134
+ ├── notifications.client.ts # Client-side notification renderers
135
+ └── encryption.ts # Tenant data encryption maps (defaultEncryptionMaps) for sensitive / GDPR fields
132
136
  ```
133
137
 
134
138
  Register in `src/modules.ts`: `{ id: '<id>', from: '@app' }`
135
139
 
140
+ ## Mandatory Module Mechanisms (every module MUST use these — no DIY substitutes)
141
+
142
+ When the user asks to **create a new application** or a **new module**, do not invent your own routing, auth, persistence, forms, or caching. The framework provides one canonical primitive for each concern. If a feature is not on this list and not in the Task → Context Map, ask before adding it — do not roll your own.
143
+
144
+ | Concern | Canonical mechanism | Where to learn it |
145
+ |---|---|---|
146
+ | Module structure & auto-discovery | `src/modules/<id>/{api,backend,frontend,data,subscribers,workers,widgets}` + `index.ts` + `src/modules.ts` (`from: '@app'`) — discovered by `yarn generate` | `.ai/skills/module-scaffold/SKILL.md`, `.ai/guides/core.md` → Module Files; <https://docs.open-mercato.dev/framework/modules/overview> |
147
+ | **Backend admin pages** | Auto-discovered files under `src/modules/<id>/backend/**`, paired `page.meta.ts` with `requireAuth` + `requireFeatures` + `pageGroup`/`pageGroupKey`/`pageOrder` | `.ai/skills/backend-ui-design/SKILL.md`, `.ai/skills/module-scaffold/references/navigation-patterns.md`; <https://docs.open-mercato.dev/framework/modules/routes-and-pages> |
148
+ | **Frontend public pages** | Auto-discovered files under `src/modules/<id>/frontend/**`. Customer portal pages live under `frontend/[orgSlug]/portal/<path>/page.tsx` with `requireCustomerAuth` / `requireCustomerFeatures` in `page.meta.ts` | `.ai/guides/ui.md` → Portal Extension; <https://docs.open-mercato.dev/framework/modules/routes-and-pages> |
149
+ | **API routes** | Files under `src/modules/<id>/api/**/route.ts` exporting handlers + `metadata` (per-method `requireAuth` / `requireFeatures`) + `openApi`. NEVER write a top-level `export const requireAuth` — the registry no longer recognises it | `.ai/guides/core.md` → API Routes; <https://docs.open-mercato.dev/framework/api/api-development-guide> |
150
+ | **CRUD APIs (factory)** | `makeCrudRoute({ entity, entityId, operations, schema, indexer: { entityType } })` from `@open-mercato/shared/lib/crud/factory`. Always set `indexer` so query-index coverage stays correct. Custom (non-`makeCrudRoute`) write routes MUST call `validateCrudMutationGuard` before the mutation and `runCrudMutationGuardAfterSuccess` after success | `.ai/skills/module-scaffold/SKILL.md` → Create API Routes; <https://docs.open-mercato.dev/framework/api/crud-factory> |
151
+ | **CRUD forms in admin** | `<CrudForm entityId apiPath mode fields />` from `@open-mercato/ui/backend/CrudForm`; helpers `createCrud` / `updateCrud` / `deleteCrud` from `@open-mercato/ui/backend/utils/crud`; `createCrudFormError` from `@open-mercato/ui/backend/utils/serverErrors`. Never raw `<form>`, never raw `fetch` | `.ai/skills/backend-ui-design/SKILL.md`; <https://docs.open-mercato.dev/framework/admin-ui/crud-form> |
152
+ | **DataTables in admin** | `<DataTable entityId apiPath title columns />` from `@open-mercato/ui/backend/DataTable`. Keep `entityId` and `extensionTableId` stable so widget injection (columns, row actions, bulk actions, filters, toolbar) keeps working | `.ai/skills/backend-ui-design/SKILL.md`; <https://docs.open-mercato.dev/framework/admin-ui/data-grids> |
153
+ | **Authorization (RBAC)** | Declare features in `<module>/acl.ts`, grant them in `<module>/setup.ts` `defaultRoleFeatures`, gate pages and routes with `requireFeatures` in `metadata` / `page.meta.ts`. NEVER use `requireRoles` (role names mutate). Run `yarn mercato auth sync-role-acls` after adding new features | `.ai/guides/core.md` → Access Control; <https://docs.open-mercato.dev/framework/rbac/overview> |
154
+ | **Multi-tenant scoping (default for every entity)** | Every tenant-scoped entity MUST include indexed `organization_id` and `tenant_id` columns and every read/write MUST filter by them. The CRUD factory injects scope automatically — do NOT bypass it. For ad-hoc queries use `withScopedPayload` from `@open-mercato/shared/lib/api/scoped` | `.ai/skills/data-model-design/SKILL.md`; <https://docs.open-mercato.dev/architecture/system-overview> |
155
+ | **Encryption maps for sensitive data** | Declare a module-level `<module>/encryption.ts` exporting `defaultEncryptionMaps: ModuleEncryptionMap[]` from `@open-mercato/shared/modules/encryption`. Read encrypted columns via `findWithDecryption` / `findOneWithDecryption` from `@open-mercato/shared/lib/encryption/find`. NEVER hand-roll AES/KMS, NEVER use `em.find` on encrypted columns | `.ai/skills/data-model-design/SKILL.md` → Sensitive Data and Encryption Maps; <https://docs.open-mercato.dev/user-guide/encryption> |
156
+ | **Cache** | Resolve the cache from DI (`container.resolve('cache')`) — never `new Redis(...)` or raw SQLite. Tag with `tenant:<id>` / `org:<id>` and the entity-type tag so invalidation stays tenant-scoped | `.ai/guides/cache.md`; <https://docs.open-mercato.dev/user-guide/cache-management> |
157
+ | **Background workers** | `src/modules/<id>/workers/*.ts` exporting `metadata: { queue, id?, concurrency? }` + default handler. Never spin up custom queues | `.ai/guides/queue.md`; <https://docs.open-mercato.dev/framework/events/queue-workers> |
158
+ | **Events between modules** | `<module>/events.ts` with `createModuleEvents({ moduleId, events } as const)`. Subscribers in `subscribers/*.ts`. Never call other modules' services directly across module boundaries | `.ai/guides/events.md`; <https://docs.open-mercato.dev/framework/events/overview> |
159
+ | **i18n (every user-facing string)** | `useT()` client-side from `@open-mercato/shared/lib/i18n/context`, `resolveTranslations()` server-side from `@open-mercato/shared/lib/i18n/server`; keys in `src/i18n/<locale>.json`. Never hard-code labels in components | `.ai/guides/shared.md` → i18n |
160
+
161
+ > Rule of thumb: if you find yourself reaching for raw `fetch`, raw `<form>`, ad-hoc `crypto`, ad-hoc `Redis`, or a manual `m2m` join across modules, stop and check the row above — there is a canonical helper.
162
+
136
163
  ## CRITICAL rules — always follow without exception
137
164
 
138
165
  1. **Entity classes live in `src/modules/<module>/data/entities.ts` and MUST import decorators from `@mikro-orm/decorators/legacy`.** Start there for every schema change.
@@ -154,9 +181,16 @@ Register in `src/modules.ts`: `{ id: '<id>', from: '@app' }`
154
181
  **Dashboards fallback rule.** When the user (or the `trim-unused-modules` skill) disables the `dashboards` module, you MUST update `src/app/(backend)/backend/page.tsx` so it no longer renders `<DashboardScreen />`. Replace the dashboard render with a `redirect(...)` to the first enabled backend page for the current user — preferring pages already registered in the main sidebar group and respecting the admin/superadmin role of the caller. Otherwise `/backend` will crash at build or request time because the removed module no longer ships `DashboardScreen`. Always fall back to `/backend/profile` only if no other backend page is available.
155
182
  9. **New features MUST be visible to default roles immediately.** Every time you add a new feature ID (e.g. `my_module.view`, `my_module.manage`) to `src/modules/<module>/acl.ts`, you MUST also (a) add that feature to `defaultRoleFeatures` in the same module's `setup.ts` so the admin role and any other appropriate default roles get it on every tenant setup; and (b) run `yarn mercato auth sync-role-acls` so existing tenants pick up the new feature without a reinstall. Use `--tenant <tenantId>` only when the user asks to target one tenant. Do this automatically unless the user has explicitly said otherwise — the user should see the features you are building, not stare at a blank admin because their role is missing a grant. Feature IDs are FROZEN once shipped; if a rename is required, add the new ID alongside, grant it, and keep the old one as a deprecated alias.
156
183
  10. **Strict Design System alignment for every UI change.** Any UI you add or edit MUST use the Open Mercato design system components and tokens. No hardcoded Tailwind status colors (`text-red-500`, `bg-green-100`, etc.) — use semantic tokens (`text-status-error-text`, `bg-status-success-bg`, …). No arbitrary text sizes (`text-[11px]`, `text-[13px]`) — use the Tailwind scale (`text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`) or the `text-overline` token for 11px uppercase labels. In PAGE BODY UI, use `lucide-react` icons (never inline `<svg>`). Use `StatusBadge` for entity status, `Alert` for inline feedback, `FormField` for standalone form inputs, `SectionHeader` for detail-page section headings, `CollapsibleSection` for collapsible regions, `LoadingMessage`/`Spinner`/`DataLoader` for async states, and `EmptyState` (or DataTable's `emptyState` prop) for empty lists. For list pages, follow `.ai/skills/backend-ui-design/SKILL.md` and prefer the `DataTable` host pattern shown there (`entityId`, `apiPath`, stable `extensionTableId`, and explicit pagination props when you own the data source). Every dialog MUST support `Cmd/Ctrl+Enter` to submit and `Escape` to cancel. Every icon-only button MUST have an `aria-label`. These rules apply to `src/modules/<module>/backend/**` and `src/modules/<module>/frontend/**` alike.
157
- 11. **BEFORE writing ANY code**, you MUST:
184
+ 11. **Sensitive / GDPR fields MUST go through the encryption-maps mechanism — never hand-rolled.** The framework provides per-tenant DEKs, KMS-backed key resolution, and a declarative field-level map. Whenever the user asks for "this field encrypted", "store this securely", "this is PII", "GDPR", "encryption at rest", or you are designing a column that will hold names, addresses, contacts, free-text notes about people, integration secrets/credentials, or any data subject to a data-processing agreement, you MUST:
185
+ - Declare the entity + field list in `src/modules/<module>/encryption.ts` exporting `defaultEncryptionMaps: ModuleEncryptionMap[]` (type imported from `@open-mercato/shared/modules/encryption`).
186
+ - Read those columns via `findWithDecryption` / `findOneWithDecryption` from `@open-mercato/shared/lib/encryption/find` (passing `tenantId` and `organizationId`). Never use raw `em.find` on encrypted columns.
187
+ - For deterministic-lookup fields (e.g., login email), declare a sibling `hashField` in the map so equality lookups still work.
188
+ - Run `yarn mercato entities seed-encryption --tenant <tenantId>` after adding maps so existing tenants pick them up; new tenants get them automatically during `auth:setup`.
189
+ - Treat hand-rolled AES, raw `crypto.subtle`, custom KMS calls, or storing plaintext "for now" as broken — rewrite via the maps. See `.ai/skills/data-model-design/SKILL.md` → Sensitive Data and Encryption Maps and <https://docs.open-mercato.dev/user-guide/encryption>.
190
+ 12. **BEFORE writing ANY code**, you MUST:
158
191
  - Match your task against the **Task → Context Map** above
159
192
  - `Read` every file listed in the "Load" column for your task type
193
+ - Read the **Mandatory Module Mechanisms** section above to confirm which canonical primitives apply (CRUD factory, CrudForm, DataTable, RBAC, multi-tenant scoping, encryption maps, cache, events) — do not invent your own substitutes
160
194
  - Only then proceed to implementation
161
195
  - If your task matches multiple rows, load ALL listed files
162
196
  - **Do NOT skip this step.** The guides contain canonical import paths, required patterns, and conventions that CANNOT be reliably inferred from existing code alone. Skipping leads to wrong imports, missing conventions, and rework.
@@ -195,7 +229,8 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
195
229
  import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
196
230
 
197
231
  // CRUD forms
198
- import { CrudForm, createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/crud'
232
+ import { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'
233
+ import { createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
199
234
  import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
200
235
 
201
236
  // UI components (MUST use — never raw <button>)
@@ -1,9 +1,14 @@
1
1
  import { defineConfig } from '@playwright/test'
2
2
  import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
3
4
  import { discoverIntegrationSpecFiles } from '@open-mercato/cli/lib/testing/integration-discovery'
4
5
 
5
6
  const captureScreenshots = process.env.PW_CAPTURE_SCREENSHOTS === '1'
6
7
  const isGitHubActions = process.env.GITHUB_ACTIONS === 'true'
8
+ // Standalone apps generated from this template declare `"type": "module"`,
9
+ // so the CommonJS `__dirname` is undefined when Playwright loads this config
10
+ // under the Node ESM loader. Reconstruct it from `import.meta.url`.
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
7
12
  const projectRoot = path.resolve(__dirname, '..', '..', '..')
8
13
  const qaTestResultsRoot = path.join(projectRoot, '.ai', 'qa', 'test-results')
9
14
  const normalizePath = (value: string) => value.split(path.sep).join('/')