create-mercato-app 0.6.5-develop.4534.1.b459babe6d → 0.6.5-develop.4544.1.71c003c861

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.
@@ -126,7 +126,7 @@ export class <Entity> {
126
126
  - Table name: **plural, snake_case** — matches module ID
127
127
  - PK: always `uuid` with `v4()` default
128
128
  - MUST include `organization_id` + `tenant_id` with `@Index()`
129
- - MUST include `created_at`, `updated_at`, `deleted_at`, `is_active`
129
+ - MUST include `created_at`, `updated_at`, `deleted_at`, `is_active`. The `updated_at` column is what OSS **optimistic locking** (default ON) compares — keep it on every user-editable entity, and make your CRUD GET/list responses return `updatedAt` so the UI can send the expected version.
130
130
  - Entity decorators MUST come from `@mikro-orm/decorators/legacy`
131
131
  - Cross-module references: store FK as `uuid` field (e.g., `customer_id`) — never use ORM `@ManyToOne`
132
132
  - Use `@Property({ type: 'jsonb' })` for flexible/nested data
@@ -234,6 +234,8 @@ export const openApi = {
234
234
 
235
235
  Use `CrudForm` and `DataTable` from `@open-mercato/ui`. See the `backend-ui-design` skill for full component reference.
236
236
 
237
+ > **Optimistic locking (default ON).** `CrudForm` in edit mode auto-derives the expected-version header from `initialValues.updatedAt` and applies it to **both** save and delete — so pass the loaded record's `updatedAt` into `initialValues`. For custom (non-`CrudForm`) list-row deletes or dialog mutations, wrap the call with `withScopedApiRequestHeaders(buildOptimisticLockHeader(record.updatedAt), () => deleteCrud(...))` and surface the 409 with `surfaceRecordConflict(err, t)` from `@open-mercato/ui/backend/conflicts`. Never leave a mutating edit/delete UI without a version header — concurrent edits would silently overwrite.
238
+
237
239
  ### Page Metadata & Sidebar Navigation
238
240
 
239
241
  **File**: `src/modules/<module_id>/backend/page.meta.ts`
@@ -126,7 +126,7 @@ export class <Entity> {
126
126
  - Table name: **plural, snake_case** — matches module ID
127
127
  - PK: always `uuid` with `v4()` default
128
128
  - MUST include `organization_id` + `tenant_id` with `@Index()`
129
- - MUST include `created_at`, `updated_at`, `deleted_at`, `is_active`
129
+ - MUST include `created_at`, `updated_at`, `deleted_at`, `is_active`. The `updated_at` column is what OSS **optimistic locking** (default ON) compares — keep it on every user-editable entity, and make your CRUD GET/list responses return `updatedAt` so the UI can send the expected version.
130
130
  - Entity decorators MUST come from `@mikro-orm/decorators/legacy`
131
131
  - Cross-module references: store FK as `uuid` field (e.g., `customer_id`) — never use ORM `@ManyToOne`
132
132
  - Use `@Property({ type: 'jsonb' })` for flexible/nested data
@@ -234,6 +234,8 @@ export const openApi = {
234
234
 
235
235
  Use `CrudForm` and `DataTable` from `@open-mercato/ui`. See the `backend-ui-design` skill for full component reference.
236
236
 
237
+ > **Optimistic locking (default ON).** `CrudForm` in edit mode auto-derives the expected-version header from `initialValues.updatedAt` and applies it to **both** save and delete — so pass the loaded record's `updatedAt` into `initialValues`. For custom (non-`CrudForm`) list-row deletes or dialog mutations, wrap the call with `withScopedApiRequestHeaders(buildOptimisticLockHeader(record.updatedAt), () => deleteCrud(...))` and surface the 409 with `surfaceRecordConflict(err, t)` from `@open-mercato/ui/backend/conflicts`. Never leave a mutating edit/delete UI without a version header — concurrent edits would silently overwrite.
238
+
237
239
  ### Page Metadata & Sidebar Navigation
238
240
 
239
241
  **File**: `src/modules/<module_id>/backend/page.meta.ts`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mercato-app",
3
- "version": "0.6.5-develop.4534.1.b459babe6d",
3
+ "version": "0.6.5-develop.4544.1.71c003c861",
4
4
  "type": "module",
5
5
  "description": "Create a new Open Mercato application",
6
6
  "main": "./dist/index.js",
@@ -214,6 +214,7 @@ Install official package-backed modules with `yarn mercato module add @open-merc
214
214
 
215
215
  - Define module entities in `src/modules/<module>/data/entities.ts`.
216
216
  - Import entity decorators from `@mikro-orm/decorators/legacy`, not `@mikro-orm/core`.
217
+ - User-editable entities MUST carry an `updated_at` column (onCreate+onUpdate) — it backs OSS **optimistic locking** (default ON). Return `updatedAt` from CRUD list/detail responses, and on edit/delete UI let `CrudForm` auto-derive the version header from `initialValues.updatedAt`, or for custom handlers wrap with `withScopedApiRequestHeaders(buildOptimisticLockHeader(record.updatedAt), …)` + `surfaceRecordConflict(err, t)`. Without it, concurrent edits silently overwrite.
217
218
  - Treat `yarn db:generate` as a schema-diff probe. Default to the generated SQL, but if it emits unrelated churn, keep or write only the scoped SQL for the module you are changing and update `src/modules/<module>/migrations/.snapshot-open-mercato.json` in the same change.
218
219
 
219
220
  ### API Route Files MUST Export `metadata`