@open-mercato/core 0.6.7-develop.6604.1.2c49816dff → 0.6.7-develop.6605.1.761dd4c810
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/dist/modules/entities/lib/field-definitions.js +1 -0
- package/dist/modules/entities/lib/field-definitions.js.map +2 -2
- package/dist/modules/entities/lib/install-from-ce.js +11 -2
- package/dist/modules/entities/lib/install-from-ce.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/entities/lib/field-definitions.ts +1 -0
- package/src/modules/entities/lib/install-from-ce.ts +17 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/entities/lib/field-definitions.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomFieldDef } from '../data/entities'\nimport type { CustomFieldDefinition } from '@open-mercato/shared/modules/entities'\n\nexport type FieldSetInput = {\n entity: string\n fields: CustomFieldDefinition[]\n source?: string\n}\n\nexport type EnsureFieldDefinitionsOptions = {\n organizationId: string | null\n tenantId: string | null\n dryRun?: boolean\n createOnly?: boolean\n}\n\nexport type EnsureFieldDefinitionsResult = {\n created: number\n updated: number\n unchanged: number\n}\n\nconst CONFIG_PASSTHROUGH_KEYS: Array<keyof CustomFieldDefinition> = [\n 'label',\n 'description',\n 'fieldset',\n 'fieldsets',\n 'group',\n 'options',\n 'optionsUrl',\n 'defaultValue',\n 'required',\n 'multi',\n 'filterable',\n 'formEditable',\n 'listVisible',\n 'indexed',\n 'editor',\n 'input',\n 'relatedEntityId',\n 'dictionaryId',\n 'dictionaryInlineCreate',\n 'validation',\n 'maxAttachmentSizeMb',\n 'acceptExtensions',\n 'sourceMetadata',\n]\n\nfunction normalizeValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map((item) => normalizeValue(item))\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>)\n .sort((a, b) => a.localeCompare(b))\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = normalizeValue((value as Record<string, unknown>)[key])\n return acc\n }, {})\n }\n return value\n}\n\nfunction configEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(normalizeValue(a ?? null)) === JSON.stringify(normalizeValue(b ?? null))\n}\n\nexport async function ensureCustomFieldDefinitions(\n em: EntityManager,\n sets: FieldSetInput[],\n scope: EnsureFieldDefinitionsOptions\n): Promise<EnsureFieldDefinitionsResult> {\n let created = 0\n let updated = 0\n let unchanged = 0\n\n // Prefetch every existing definition the batch could touch in a single query,\n // then index by composite key so the nested loop never issues per-field lookups.\n const entityIds = Array.from(new Set(sets.map((set) => set.entity)))\n const fieldKeys = Array.from(new Set(sets.flatMap((set) => set.fields.map((field) => field.key))))\n const existingByKey = new Map<string, CustomFieldDef>()\n if (entityIds.length > 0 && fieldKeys.length > 0) {\n const existingDefs = await em.find(CustomFieldDef, {\n entityId: { $in: entityIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n key: { $in: fieldKeys },\n })\n for (const def of existingDefs) {\n existingByKey.set(`${def.entityId}|${def.key}`, def)\n }\n }\n\n let dirty = false\n\n for (const set of sets) {\n for (const field of set.fields) {\n const existing = existingByKey.get(`${set.entity}|${field.key}`) ?? null\n const configJson: Record<string, unknown> = {}\n\n for (const key of CONFIG_PASSTHROUGH_KEYS) {\n const value = field[key]\n if (value !== undefined) configJson[key] = value as unknown\n }\n\n if (!existing) {\n if (!scope.dryRun) {\n const createdDef = em.create(CustomFieldDef, {\n entityId: set.entity,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n key: field.key,\n kind: field.kind,\n configJson,\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(createdDef)\n // Track so duplicate (entity, key) pairs within the batch update in memory instead of double-inserting.\n existingByKey.set(`${set.entity}|${field.key}`, createdDef)\n dirty = true\n }\n created++\n continue\n }\n\n const kindChanged = existing.kind !== field.kind\n const configChanged = !configEquals(existing.configJson ?? null, configJson)\n const needsActivation = existing.isActive !== true || existing.deletedAt != null\n if (scope.createOnly) {\n unchanged++\n continue\n }\n if (!kindChanged && !configChanged && !needsActivation) {\n unchanged++\n continue\n }\n\n if (!scope.dryRun) {\n existing.kind = field.kind\n ;(existing as any).configJson = configJson\n existing.isActive = true\n existing.updatedAt = new Date()\n if (existing.deletedAt) existing.deletedAt = null\n em.persist(existing)\n dirty = true\n }\n updated++\n }\n }\n\n if (dirty) {\n // Single flush for the whole batch instead of one round trip per field.\n await em.flush()\n }\n\n return { created, updated, unchanged }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CustomFieldDef } from '../data/entities'\nimport type { CustomFieldDefinition } from '@open-mercato/shared/modules/entities'\n\nexport type FieldSetInput = {\n entity: string\n fields: CustomFieldDefinition[]\n source?: string\n}\n\nexport type EnsureFieldDefinitionsOptions = {\n organizationId: string | null\n tenantId: string | null\n dryRun?: boolean\n createOnly?: boolean\n}\n\nexport type EnsureFieldDefinitionsResult = {\n created: number\n updated: number\n unchanged: number\n}\n\nconst CONFIG_PASSTHROUGH_KEYS: Array<keyof CustomFieldDefinition> = [\n 'label',\n 'description',\n 'fieldset',\n 'fieldsets',\n 'group',\n 'options',\n 'optionsUrl',\n 'defaultValue',\n 'required',\n 'multi',\n 'filterable',\n 'formEditable',\n 'listVisible',\n 'indexed',\n 'priority',\n 'editor',\n 'input',\n 'relatedEntityId',\n 'dictionaryId',\n 'dictionaryInlineCreate',\n 'validation',\n 'maxAttachmentSizeMb',\n 'acceptExtensions',\n 'sourceMetadata',\n]\n\nfunction normalizeValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map((item) => normalizeValue(item))\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>)\n .sort((a, b) => a.localeCompare(b))\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = normalizeValue((value as Record<string, unknown>)[key])\n return acc\n }, {})\n }\n return value\n}\n\nfunction configEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(normalizeValue(a ?? null)) === JSON.stringify(normalizeValue(b ?? null))\n}\n\nexport async function ensureCustomFieldDefinitions(\n em: EntityManager,\n sets: FieldSetInput[],\n scope: EnsureFieldDefinitionsOptions\n): Promise<EnsureFieldDefinitionsResult> {\n let created = 0\n let updated = 0\n let unchanged = 0\n\n // Prefetch every existing definition the batch could touch in a single query,\n // then index by composite key so the nested loop never issues per-field lookups.\n const entityIds = Array.from(new Set(sets.map((set) => set.entity)))\n const fieldKeys = Array.from(new Set(sets.flatMap((set) => set.fields.map((field) => field.key))))\n const existingByKey = new Map<string, CustomFieldDef>()\n if (entityIds.length > 0 && fieldKeys.length > 0) {\n const existingDefs = await em.find(CustomFieldDef, {\n entityId: { $in: entityIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n key: { $in: fieldKeys },\n })\n for (const def of existingDefs) {\n existingByKey.set(`${def.entityId}|${def.key}`, def)\n }\n }\n\n let dirty = false\n\n for (const set of sets) {\n for (const field of set.fields) {\n const existing = existingByKey.get(`${set.entity}|${field.key}`) ?? null\n const configJson: Record<string, unknown> = {}\n\n for (const key of CONFIG_PASSTHROUGH_KEYS) {\n const value = field[key]\n if (value !== undefined) configJson[key] = value as unknown\n }\n\n if (!existing) {\n if (!scope.dryRun) {\n const createdDef = em.create(CustomFieldDef, {\n entityId: set.entity,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n key: field.key,\n kind: field.kind,\n configJson,\n isActive: true,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n em.persist(createdDef)\n // Track so duplicate (entity, key) pairs within the batch update in memory instead of double-inserting.\n existingByKey.set(`${set.entity}|${field.key}`, createdDef)\n dirty = true\n }\n created++\n continue\n }\n\n const kindChanged = existing.kind !== field.kind\n const configChanged = !configEquals(existing.configJson ?? null, configJson)\n const needsActivation = existing.isActive !== true || existing.deletedAt != null\n if (scope.createOnly) {\n unchanged++\n continue\n }\n if (!kindChanged && !configChanged && !needsActivation) {\n unchanged++\n continue\n }\n\n if (!scope.dryRun) {\n existing.kind = field.kind\n ;(existing as any).configJson = configJson\n existing.isActive = true\n existing.updatedAt = new Date()\n if (existing.deletedAt) existing.deletedAt = null\n em.persist(existing)\n dirty = true\n }\n updated++\n }\n }\n\n if (dirty) {\n // Single flush for the whole batch instead of one round trip per field.\n await em.flush()\n }\n\n return { created, updated, unchanged }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -17,6 +17,7 @@ const FIELD_DETAIL_KEYS = [
|
|
|
17
17
|
"formEditable",
|
|
18
18
|
"listVisible",
|
|
19
19
|
"indexed",
|
|
20
|
+
"priority",
|
|
20
21
|
"editor",
|
|
21
22
|
"input",
|
|
22
23
|
"relatedEntityId",
|
|
@@ -71,12 +72,19 @@ function buildAggregatedConfigs() {
|
|
|
71
72
|
}
|
|
72
73
|
function resolveFields(fieldSets) {
|
|
73
74
|
const byKey = /* @__PURE__ */ new Map();
|
|
75
|
+
const declarationOrder = /* @__PURE__ */ new Map();
|
|
74
76
|
for (const set of fieldSets) {
|
|
75
77
|
for (const field of set.fields ?? []) {
|
|
78
|
+
if (!declarationOrder.has(field.key)) declarationOrder.set(field.key, declarationOrder.size);
|
|
76
79
|
byKey.set(field.key, { ...field });
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
|
-
|
|
82
|
+
const resolved = Array.from(byKey.values()).map((field) => typeof field.priority === "number" ? field : { ...field, priority: declarationOrder.get(field.key) ?? 0 });
|
|
83
|
+
return resolved.sort((a, b) => {
|
|
84
|
+
const byPriority = (a.priority ?? 0) - (b.priority ?? 0);
|
|
85
|
+
if (byPriority !== 0) return byPriority;
|
|
86
|
+
return (declarationOrder.get(a.key) ?? 0) - (declarationOrder.get(b.key) ?? 0);
|
|
87
|
+
});
|
|
80
88
|
}
|
|
81
89
|
function normalizeField(field) {
|
|
82
90
|
const payload = {
|
|
@@ -247,6 +255,7 @@ function getAggregatedCustomEntityConfigs() {
|
|
|
247
255
|
export {
|
|
248
256
|
getAggregatedCustomEntityConfigs,
|
|
249
257
|
installCustomEntitiesFromModules,
|
|
250
|
-
listCustomEntityIds
|
|
258
|
+
listCustomEntityIds,
|
|
259
|
+
resolveFields
|
|
251
260
|
};
|
|
252
261
|
//# sourceMappingURL=install-from-ce.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/entities/lib/install-from-ce.ts"],
|
|
4
|
-
"sourcesContent": ["import crypto from 'node:crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache/types'\nimport type { CustomFieldDefinition, CustomFieldSet, CustomEntitySpec } from '@open-mercato/shared/modules/entities'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { ensureCustomFieldDefinitions } from './field-definitions'\nimport { upsertCustomEntity, type UpsertCustomEntityResult } from './register'\nimport { invalidateDefinitionsCache } from '../api/definitions.cache'\n\ntype InstallScope = {\n tenantId: string | null\n}\n\nexport type InstallEntitiesOptions = {\n tenantIds?: string[]\n entityIds?: string[]\n includeGlobal?: boolean\n dryRun?: boolean\n createOnly?: boolean\n force?: boolean\n logger?: (message: string) => void\n}\n\nexport type InstallEntitiesResult = {\n processed: number\n synchronized: number\n skipped: number\n fieldChanges: number\n}\n\nexport type AggregatedEntityConfig = {\n entityId: string\n moduleIds: Set<string>\n spec?: CustomEntitySpec\n fieldSets: CustomFieldSet[]\n}\n\nconst FIELD_DETAIL_KEYS: Array<keyof CustomFieldDefinition> = [\n 'label',\n 'description',\n 'options',\n 'optionsUrl',\n 'defaultValue',\n 'required',\n 'multi',\n 'filterable',\n 'formEditable',\n 'listVisible',\n 'indexed',\n 'editor',\n 'input',\n 'relatedEntityId',\n 'validation',\n 'maxAttachmentSizeMb',\n 'acceptExtensions',\n]\n\nfunction normalizeValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map((item) => normalizeValue(item))\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>)\n .sort((a, b) => a.localeCompare(b))\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = normalizeValue((value as Record<string, unknown>)[key])\n return acc\n }, {})\n }\n return value\n}\n\nfunction computeChecksum(payload: unknown): string {\n return crypto.createHash('md5').update(JSON.stringify(normalizeValue(payload))).digest('hex')\n}\n\nfunction systemEntityIds(): Set<string> {\n const ids = new Set<string>()\n const GeneratedEntities = getEntityIds()\n for (const moduleEntities of Object.values(GeneratedEntities)) {\n for (const id of Object.values(moduleEntities as Record<string, string>)) {\n ids.add(id)\n }\n }\n return ids\n}\n\nfunction buildAggregatedConfigs(): AggregatedEntityConfig[] {\n const map = new Map<string, AggregatedEntityConfig>()\n const modules = getModules()\n for (const mod of modules) {\n const moduleId = mod.id\n const entitySpecs = ((mod as any).customEntities as CustomEntitySpec[] | undefined) ?? []\n for (const spec of entitySpecs) {\n const existing = map.get(spec.id) ?? { entityId: spec.id, moduleIds: new Set<string>(), spec: undefined, fieldSets: [] }\n existing.moduleIds.add(moduleId)\n if (!existing.spec) existing.spec = spec\n map.set(spec.id, existing)\n }\n const fieldSets = ((mod as any).customFieldSets as CustomFieldSet[] | undefined) ?? []\n for (const set of fieldSets) {\n const existing = map.get(set.entity) ?? { entityId: set.entity, moduleIds: new Set<string>(), spec: undefined, fieldSets: [] }\n existing.moduleIds.add(moduleId)\n existing.fieldSets.push(set)\n map.set(set.entity, existing)\n }\n }\n return Array.from(map.values())\n}\n\nfunction resolveFields(fieldSets: CustomFieldSet[]): CustomFieldDefinition[] {\n const byKey = new Map<string, CustomFieldDefinition>()\n for (const set of fieldSets) {\n for (const field of set.fields ?? []) {\n byKey.set(field.key, { ...field })\n }\n }\n return Array.from(byKey.values()).sort((a, b) => a.key.localeCompare(b.key))\n}\n\nfunction normalizeField(field: CustomFieldDefinition) {\n const payload: Record<string, unknown> = {\n key: field.key,\n kind: field.kind,\n }\n for (const key of FIELD_DETAIL_KEYS) {\n const value = field[key]\n if (value !== undefined) payload[key] = value as unknown\n }\n if (field.id) payload.id = field.id\n return payload\n}\n\nfunction buildChecksumPayload(params: {\n entityId: string\n scope: InstallScope\n spec: CustomEntitySpec | undefined\n global: boolean\n fields: CustomFieldDefinition[]\n}) {\n const { entityId, scope, spec, global, fields } = params\n return {\n entityId,\n scope,\n global,\n label: spec?.label ?? null,\n description: spec?.description ?? null,\n labelField: spec?.labelField ?? null,\n defaultEditor: spec?.defaultEditor ?? null,\n showInSidebar: spec?.showInSidebar ?? false,\n accessRestricted: spec?.accessRestricted ?? false,\n fields: fields.map((f) => normalizeField(f)),\n }\n}\n\nexport async function installCustomEntitiesFromModules(\n em: EntityManager,\n cache: CacheStrategy | null | undefined,\n options: InstallEntitiesOptions = {}\n): Promise<InstallEntitiesResult> {\n const requestedEntityIds = options.entityIds\n ? new Set(options.entityIds.filter((id): id is string => typeof id === 'string' && id.length > 0))\n : null\n const aggregated = buildAggregatedConfigs().filter((entry) => {\n if (!requestedEntityIds) return true\n return requestedEntityIds.has(entry.entityId)\n })\n const systemIds = systemEntityIds()\n const includeGlobal = options.includeGlobal !== false\n const dryRun = options.dryRun === true\n const createOnly = options.createOnly === true\n const force = options.force === true\n const logger = options.logger\n\n let tenantIds: string[] | undefined\n if (options.tenantIds !== undefined) {\n tenantIds = Array.from(new Set(options.tenantIds.filter((id): id is string => typeof id === 'string' && id.length > 0)))\n }\n\n const ensureTenantIds = async (): Promise<string[]> => {\n if (tenantIds !== undefined) return tenantIds\n const rows = await em.find(Tenant, { deletedAt: null } as any, { fields: ['id'] as any })\n tenantIds = rows.map((row) => row.id)\n return tenantIds ?? []\n }\n\n let processed = 0\n let synchronized = 0\n let skipped = 0\n let fieldChanges = 0\n\n for (const entry of aggregated) {\n const { entityId } = entry\n const spec = entry.spec\n const fields = resolveFields(entry.fieldSets)\n const isSystem = systemIds.has(entityId)\n const registerEntity = !isSystem && !!spec\n const isGlobal = spec?.global === true\n\n const scopes: InstallScope[] = []\n if (isGlobal) {\n if (includeGlobal) scopes.push({ tenantId: null })\n } else {\n const ids = await ensureTenantIds()\n if (!ids.length) {\n skipped++\n continue\n }\n for (const tenantId of ids) scopes.push({ tenantId })\n }\n\n if (!scopes.length && fields.length === 0 && !registerEntity) {\n skipped++\n continue\n }\n\n for (const scope of scopes) {\n processed++\n const scopeKey = scope.tenantId ? `tenant:${scope.tenantId}` : 'global'\n const checksumPayload = buildChecksumPayload({\n entityId,\n scope,\n spec,\n global: isGlobal,\n fields,\n })\n const checksum = computeChecksum(checksumPayload)\n const cacheKey = `custom-entities:v1:${scopeKey}:${entityId}`\n\n if (!dryRun && !force && cache) {\n try {\n const cached = await cache.get(cacheKey)\n if (typeof cached === 'string' && cached === checksum) {\n skipped++\n continue\n }\n } catch {}\n }\n\n let entityResult: UpsertCustomEntityResult = 'unchanged'\n if (registerEntity) {\n entityResult = await upsertCustomEntity(em, entityId, {\n label: spec?.label ?? entityId,\n description: spec?.description ?? null,\n organizationId: null,\n tenantId: scope.tenantId,\n showInSidebar: spec?.showInSidebar ?? false,\n accessRestricted: spec?.accessRestricted ?? false,\n labelField: spec?.labelField ?? null,\n defaultEditor: spec?.defaultEditor ?? null,\n isActive: true,\n dryRun,\n createOnly,\n })\n }\n\n let fieldResult = { created: 0, updated: 0, unchanged: 0 }\n if (fields.length) {\n fieldResult = await ensureCustomFieldDefinitions(\n em,\n [{ entity: entityId, fields }],\n { organizationId: null, tenantId: scope.tenantId, dryRun, createOnly }\n )\n }\n\n const changed = (entityResult !== 'unchanged') || fieldResult.created > 0 || fieldResult.updated > 0\n const shouldInvalidateDefinitions = !dryRun && (changed || force)\n if (shouldInvalidateDefinitions) {\n await invalidateDefinitionsCache(cache ?? undefined, {\n tenantId: scope.tenantId,\n organizationId: null,\n entityIds: [entityId],\n })\n }\n\n if (changed) {\n synchronized++\n fieldChanges += fieldResult.created + fieldResult.updated\n if (!dryRun && cache) {\n try {\n await cache.set(cacheKey, checksum, { tags: [`custom-entity:${entityId}`, `custom-entity-scope:${scopeKey}`] })\n } catch {}\n }\n if (logger) {\n const parts: string[] = []\n if (entityResult !== 'unchanged') parts.push(`entity ${entityResult}`)\n if (fieldResult.created || fieldResult.updated) {\n parts.push(`fields +${fieldResult.created} / ~${fieldResult.updated}`)\n }\n logger(`Synced ${entityId} for ${scopeKey}${parts.length ? ` (${parts.join(', ')})` : ''}`)\n }\n } else {\n skipped++\n if (!dryRun && cache) {\n try {\n await cache.set(cacheKey, checksum, { tags: [`custom-entity:${entityId}`, `custom-entity-scope:${scopeKey}`] })\n } catch {}\n }\n }\n }\n }\n\n return { processed, synchronized, skipped, fieldChanges }\n}\n\nexport function listCustomEntityIds(): string[] {\n return buildAggregatedConfigs().map((entry) => entry.entityId)\n}\n\nexport function getAggregatedCustomEntityConfigs(): AggregatedEntityConfig[] {\n return buildAggregatedConfigs()\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,YAAY;AAInB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,oCAAoC;AAC7C,SAAS,0BAAyD;AAClE,SAAS,kCAAkC;AA8B3C,MAAM,oBAAwD;AAAA,EAC5D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,eAAe,OAAyB;AAC/C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,CAAC,SAAS,eAAe,IAAI,CAAC;AACzE,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,WAAO,OAAO,KAAK,KAAgC,EAChD,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC,EACjC,OAAgC,CAAC,KAAK,QAAQ;AAC7C,UAAI,GAAG,IAAI,eAAgB,MAAkC,GAAG,CAAC;AACjE,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,SAA0B;AACjD,SAAO,OAAO,WAAW,KAAK,EAAE,OAAO,KAAK,UAAU,eAAe,OAAO,CAAC,CAAC,EAAE,OAAO,KAAK;AAC9F;AAEA,SAAS,kBAA+B;AACtC,QAAM,MAAM,oBAAI,IAAY;AAC5B,QAAM,oBAAoB,aAAa;AACvC,aAAW,kBAAkB,OAAO,OAAO,iBAAiB,GAAG;AAC7D,eAAW,MAAM,OAAO,OAAO,cAAwC,GAAG;AACxE,UAAI,IAAI,EAAE;AAAA,IACZ;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,yBAAmD;AAC1D,QAAM,MAAM,oBAAI,IAAoC;AACpD,QAAM,UAAU,WAAW;AAC3B,aAAW,OAAO,SAAS;AACzB,UAAM,WAAW,IAAI;AACrB,UAAM,cAAgB,IAAY,kBAAqD,CAAC;AACxF,eAAW,QAAQ,aAAa;AAC9B,YAAM,WAAW,IAAI,IAAI,KAAK,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,WAAW,oBAAI,IAAY,GAAG,MAAM,QAAW,WAAW,CAAC,EAAE;AACvH,eAAS,UAAU,IAAI,QAAQ;AAC/B,UAAI,CAAC,SAAS,KAAM,UAAS,OAAO;AACpC,UAAI,IAAI,KAAK,IAAI,QAAQ;AAAA,IAC3B;AACA,UAAM,YAAc,IAAY,mBAAoD,CAAC;AACrF,eAAW,OAAO,WAAW;AAC3B,YAAM,WAAW,IAAI,IAAI,IAAI,MAAM,KAAK,EAAE,UAAU,IAAI,QAAQ,WAAW,oBAAI,IAAY,GAAG,MAAM,QAAW,WAAW,CAAC,EAAE;AAC7H,eAAS,UAAU,IAAI,QAAQ;AAC/B,eAAS,UAAU,KAAK,GAAG;AAC3B,UAAI,IAAI,IAAI,QAAQ,QAAQ;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI,OAAO,CAAC;AAChC;
|
|
4
|
+
"sourcesContent": ["import crypto from 'node:crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache/types'\nimport type { CustomFieldDefinition, CustomFieldSet, CustomEntitySpec } from '@open-mercato/shared/modules/entities'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { getModules } from '@open-mercato/shared/lib/i18n/server'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { ensureCustomFieldDefinitions } from './field-definitions'\nimport { upsertCustomEntity, type UpsertCustomEntityResult } from './register'\nimport { invalidateDefinitionsCache } from '../api/definitions.cache'\n\ntype InstallScope = {\n tenantId: string | null\n}\n\nexport type InstallEntitiesOptions = {\n tenantIds?: string[]\n entityIds?: string[]\n includeGlobal?: boolean\n dryRun?: boolean\n createOnly?: boolean\n force?: boolean\n logger?: (message: string) => void\n}\n\nexport type InstallEntitiesResult = {\n processed: number\n synchronized: number\n skipped: number\n fieldChanges: number\n}\n\nexport type AggregatedEntityConfig = {\n entityId: string\n moduleIds: Set<string>\n spec?: CustomEntitySpec\n fieldSets: CustomFieldSet[]\n}\n\nconst FIELD_DETAIL_KEYS: Array<keyof CustomFieldDefinition> = [\n 'label',\n 'description',\n 'options',\n 'optionsUrl',\n 'defaultValue',\n 'required',\n 'multi',\n 'filterable',\n 'formEditable',\n 'listVisible',\n 'indexed',\n 'priority',\n 'editor',\n 'input',\n 'relatedEntityId',\n 'validation',\n 'maxAttachmentSizeMb',\n 'acceptExtensions',\n]\n\nfunction normalizeValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map((item) => normalizeValue(item))\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>)\n .sort((a, b) => a.localeCompare(b))\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = normalizeValue((value as Record<string, unknown>)[key])\n return acc\n }, {})\n }\n return value\n}\n\nfunction computeChecksum(payload: unknown): string {\n return crypto.createHash('md5').update(JSON.stringify(normalizeValue(payload))).digest('hex')\n}\n\nfunction systemEntityIds(): Set<string> {\n const ids = new Set<string>()\n const GeneratedEntities = getEntityIds()\n for (const moduleEntities of Object.values(GeneratedEntities)) {\n for (const id of Object.values(moduleEntities as Record<string, string>)) {\n ids.add(id)\n }\n }\n return ids\n}\n\nfunction buildAggregatedConfigs(): AggregatedEntityConfig[] {\n const map = new Map<string, AggregatedEntityConfig>()\n const modules = getModules()\n for (const mod of modules) {\n const moduleId = mod.id\n const entitySpecs = ((mod as any).customEntities as CustomEntitySpec[] | undefined) ?? []\n for (const spec of entitySpecs) {\n const existing = map.get(spec.id) ?? { entityId: spec.id, moduleIds: new Set<string>(), spec: undefined, fieldSets: [] }\n existing.moduleIds.add(moduleId)\n if (!existing.spec) existing.spec = spec\n map.set(spec.id, existing)\n }\n const fieldSets = ((mod as any).customFieldSets as CustomFieldSet[] | undefined) ?? []\n for (const set of fieldSets) {\n const existing = map.get(set.entity) ?? { entityId: set.entity, moduleIds: new Set<string>(), spec: undefined, fieldSets: [] }\n existing.moduleIds.add(moduleId)\n existing.fieldSets.push(set)\n map.set(set.entity, existing)\n }\n }\n return Array.from(map.values())\n}\n\nexport function resolveFields(fieldSets: CustomFieldSet[]): CustomFieldDefinition[] {\n const byKey = new Map<string, CustomFieldDefinition>()\n const declarationOrder = new Map<string, number>()\n for (const set of fieldSets) {\n for (const field of set.fields ?? []) {\n if (!declarationOrder.has(field.key)) declarationOrder.set(field.key, declarationOrder.size)\n byKey.set(field.key, { ...field })\n }\n }\n // Declaration order is the author's intent, so it drives `priority` (and the\n // resulting render order) unless a field states its own. Sorting alphabetically\n // by key here would silently discard that intent (#4378).\n const resolved = Array.from(byKey.values()).map((field) => (\n typeof field.priority === 'number'\n ? field\n : { ...field, priority: declarationOrder.get(field.key) ?? 0 }\n ))\n return resolved.sort((a, b) => {\n const byPriority = (a.priority ?? 0) - (b.priority ?? 0)\n if (byPriority !== 0) return byPriority\n return (declarationOrder.get(a.key) ?? 0) - (declarationOrder.get(b.key) ?? 0)\n })\n}\n\nfunction normalizeField(field: CustomFieldDefinition) {\n const payload: Record<string, unknown> = {\n key: field.key,\n kind: field.kind,\n }\n for (const key of FIELD_DETAIL_KEYS) {\n const value = field[key]\n if (value !== undefined) payload[key] = value as unknown\n }\n if (field.id) payload.id = field.id\n return payload\n}\n\nfunction buildChecksumPayload(params: {\n entityId: string\n scope: InstallScope\n spec: CustomEntitySpec | undefined\n global: boolean\n fields: CustomFieldDefinition[]\n}) {\n const { entityId, scope, spec, global, fields } = params\n return {\n entityId,\n scope,\n global,\n label: spec?.label ?? null,\n description: spec?.description ?? null,\n labelField: spec?.labelField ?? null,\n defaultEditor: spec?.defaultEditor ?? null,\n showInSidebar: spec?.showInSidebar ?? false,\n accessRestricted: spec?.accessRestricted ?? false,\n fields: fields.map((f) => normalizeField(f)),\n }\n}\n\nexport async function installCustomEntitiesFromModules(\n em: EntityManager,\n cache: CacheStrategy | null | undefined,\n options: InstallEntitiesOptions = {}\n): Promise<InstallEntitiesResult> {\n const requestedEntityIds = options.entityIds\n ? new Set(options.entityIds.filter((id): id is string => typeof id === 'string' && id.length > 0))\n : null\n const aggregated = buildAggregatedConfigs().filter((entry) => {\n if (!requestedEntityIds) return true\n return requestedEntityIds.has(entry.entityId)\n })\n const systemIds = systemEntityIds()\n const includeGlobal = options.includeGlobal !== false\n const dryRun = options.dryRun === true\n const createOnly = options.createOnly === true\n const force = options.force === true\n const logger = options.logger\n\n let tenantIds: string[] | undefined\n if (options.tenantIds !== undefined) {\n tenantIds = Array.from(new Set(options.tenantIds.filter((id): id is string => typeof id === 'string' && id.length > 0)))\n }\n\n const ensureTenantIds = async (): Promise<string[]> => {\n if (tenantIds !== undefined) return tenantIds\n const rows = await em.find(Tenant, { deletedAt: null } as any, { fields: ['id'] as any })\n tenantIds = rows.map((row) => row.id)\n return tenantIds ?? []\n }\n\n let processed = 0\n let synchronized = 0\n let skipped = 0\n let fieldChanges = 0\n\n for (const entry of aggregated) {\n const { entityId } = entry\n const spec = entry.spec\n const fields = resolveFields(entry.fieldSets)\n const isSystem = systemIds.has(entityId)\n const registerEntity = !isSystem && !!spec\n const isGlobal = spec?.global === true\n\n const scopes: InstallScope[] = []\n if (isGlobal) {\n if (includeGlobal) scopes.push({ tenantId: null })\n } else {\n const ids = await ensureTenantIds()\n if (!ids.length) {\n skipped++\n continue\n }\n for (const tenantId of ids) scopes.push({ tenantId })\n }\n\n if (!scopes.length && fields.length === 0 && !registerEntity) {\n skipped++\n continue\n }\n\n for (const scope of scopes) {\n processed++\n const scopeKey = scope.tenantId ? `tenant:${scope.tenantId}` : 'global'\n const checksumPayload = buildChecksumPayload({\n entityId,\n scope,\n spec,\n global: isGlobal,\n fields,\n })\n const checksum = computeChecksum(checksumPayload)\n const cacheKey = `custom-entities:v1:${scopeKey}:${entityId}`\n\n if (!dryRun && !force && cache) {\n try {\n const cached = await cache.get(cacheKey)\n if (typeof cached === 'string' && cached === checksum) {\n skipped++\n continue\n }\n } catch {}\n }\n\n let entityResult: UpsertCustomEntityResult = 'unchanged'\n if (registerEntity) {\n entityResult = await upsertCustomEntity(em, entityId, {\n label: spec?.label ?? entityId,\n description: spec?.description ?? null,\n organizationId: null,\n tenantId: scope.tenantId,\n showInSidebar: spec?.showInSidebar ?? false,\n accessRestricted: spec?.accessRestricted ?? false,\n labelField: spec?.labelField ?? null,\n defaultEditor: spec?.defaultEditor ?? null,\n isActive: true,\n dryRun,\n createOnly,\n })\n }\n\n let fieldResult = { created: 0, updated: 0, unchanged: 0 }\n if (fields.length) {\n fieldResult = await ensureCustomFieldDefinitions(\n em,\n [{ entity: entityId, fields }],\n { organizationId: null, tenantId: scope.tenantId, dryRun, createOnly }\n )\n }\n\n const changed = (entityResult !== 'unchanged') || fieldResult.created > 0 || fieldResult.updated > 0\n const shouldInvalidateDefinitions = !dryRun && (changed || force)\n if (shouldInvalidateDefinitions) {\n await invalidateDefinitionsCache(cache ?? undefined, {\n tenantId: scope.tenantId,\n organizationId: null,\n entityIds: [entityId],\n })\n }\n\n if (changed) {\n synchronized++\n fieldChanges += fieldResult.created + fieldResult.updated\n if (!dryRun && cache) {\n try {\n await cache.set(cacheKey, checksum, { tags: [`custom-entity:${entityId}`, `custom-entity-scope:${scopeKey}`] })\n } catch {}\n }\n if (logger) {\n const parts: string[] = []\n if (entityResult !== 'unchanged') parts.push(`entity ${entityResult}`)\n if (fieldResult.created || fieldResult.updated) {\n parts.push(`fields +${fieldResult.created} / ~${fieldResult.updated}`)\n }\n logger(`Synced ${entityId} for ${scopeKey}${parts.length ? ` (${parts.join(', ')})` : ''}`)\n }\n } else {\n skipped++\n if (!dryRun && cache) {\n try {\n await cache.set(cacheKey, checksum, { tags: [`custom-entity:${entityId}`, `custom-entity-scope:${scopeKey}`] })\n } catch {}\n }\n }\n }\n }\n\n return { processed, synchronized, skipped, fieldChanges }\n}\n\nexport function listCustomEntityIds(): string[] {\n return buildAggregatedConfigs().map((entry) => entry.entityId)\n}\n\nexport function getAggregatedCustomEntityConfigs(): AggregatedEntityConfig[] {\n return buildAggregatedConfigs()\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AAInB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAC7B,SAAS,oCAAoC;AAC7C,SAAS,0BAAyD;AAClE,SAAS,kCAAkC;AA8B3C,MAAM,oBAAwD;AAAA,EAC5D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,eAAe,OAAyB;AAC/C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,CAAC,SAAS,eAAe,IAAI,CAAC;AACzE,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,WAAO,OAAO,KAAK,KAAgC,EAChD,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC,EACjC,OAAgC,CAAC,KAAK,QAAQ;AAC7C,UAAI,GAAG,IAAI,eAAgB,MAAkC,GAAG,CAAC;AACjE,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,SAA0B;AACjD,SAAO,OAAO,WAAW,KAAK,EAAE,OAAO,KAAK,UAAU,eAAe,OAAO,CAAC,CAAC,EAAE,OAAO,KAAK;AAC9F;AAEA,SAAS,kBAA+B;AACtC,QAAM,MAAM,oBAAI,IAAY;AAC5B,QAAM,oBAAoB,aAAa;AACvC,aAAW,kBAAkB,OAAO,OAAO,iBAAiB,GAAG;AAC7D,eAAW,MAAM,OAAO,OAAO,cAAwC,GAAG;AACxE,UAAI,IAAI,EAAE;AAAA,IACZ;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,yBAAmD;AAC1D,QAAM,MAAM,oBAAI,IAAoC;AACpD,QAAM,UAAU,WAAW;AAC3B,aAAW,OAAO,SAAS;AACzB,UAAM,WAAW,IAAI;AACrB,UAAM,cAAgB,IAAY,kBAAqD,CAAC;AACxF,eAAW,QAAQ,aAAa;AAC9B,YAAM,WAAW,IAAI,IAAI,KAAK,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,WAAW,oBAAI,IAAY,GAAG,MAAM,QAAW,WAAW,CAAC,EAAE;AACvH,eAAS,UAAU,IAAI,QAAQ;AAC/B,UAAI,CAAC,SAAS,KAAM,UAAS,OAAO;AACpC,UAAI,IAAI,KAAK,IAAI,QAAQ;AAAA,IAC3B;AACA,UAAM,YAAc,IAAY,mBAAoD,CAAC;AACrF,eAAW,OAAO,WAAW;AAC3B,YAAM,WAAW,IAAI,IAAI,IAAI,MAAM,KAAK,EAAE,UAAU,IAAI,QAAQ,WAAW,oBAAI,IAAY,GAAG,MAAM,QAAW,WAAW,CAAC,EAAE;AAC7H,eAAS,UAAU,IAAI,QAAQ;AAC/B,eAAS,UAAU,KAAK,GAAG;AAC3B,UAAI,IAAI,IAAI,QAAQ,QAAQ;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI,OAAO,CAAC;AAChC;AAEO,SAAS,cAAc,WAAsD;AAClF,QAAM,QAAQ,oBAAI,IAAmC;AACrD,QAAM,mBAAmB,oBAAI,IAAoB;AACjD,aAAW,OAAO,WAAW;AAC3B,eAAW,SAAS,IAAI,UAAU,CAAC,GAAG;AACpC,UAAI,CAAC,iBAAiB,IAAI,MAAM,GAAG,EAAG,kBAAiB,IAAI,MAAM,KAAK,iBAAiB,IAAI;AAC3F,YAAM,IAAI,MAAM,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,IACnC;AAAA,EACF;AAIA,QAAM,WAAW,MAAM,KAAK,MAAM,OAAO,CAAC,EAAE,IAAI,CAAC,UAC/C,OAAO,MAAM,aAAa,WACtB,QACA,EAAE,GAAG,OAAO,UAAU,iBAAiB,IAAI,MAAM,GAAG,KAAK,EAAE,CAChE;AACD,SAAO,SAAS,KAAK,CAAC,GAAG,MAAM;AAC7B,UAAM,cAAc,EAAE,YAAY,MAAM,EAAE,YAAY;AACtD,QAAI,eAAe,EAAG,QAAO;AAC7B,YAAQ,iBAAiB,IAAI,EAAE,GAAG,KAAK,MAAM,iBAAiB,IAAI,EAAE,GAAG,KAAK;AAAA,EAC9E,CAAC;AACH;AAEA,SAAS,eAAe,OAA8B;AACpD,QAAM,UAAmC;AAAA,IACvC,KAAK,MAAM;AAAA,IACX,MAAM,MAAM;AAAA,EACd;AACA,aAAW,OAAO,mBAAmB;AACnC,UAAM,QAAQ,MAAM,GAAG;AACvB,QAAI,UAAU,OAAW,SAAQ,GAAG,IAAI;AAAA,EAC1C;AACA,MAAI,MAAM,GAAI,SAAQ,KAAK,MAAM;AACjC,SAAO;AACT;AAEA,SAAS,qBAAqB,QAM3B;AACD,QAAM,EAAE,UAAU,OAAO,MAAM,QAAQ,OAAO,IAAI;AAClD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,MAAM,SAAS;AAAA,IACtB,aAAa,MAAM,eAAe;AAAA,IAClC,YAAY,MAAM,cAAc;AAAA,IAChC,eAAe,MAAM,iBAAiB;AAAA,IACtC,eAAe,MAAM,iBAAiB;AAAA,IACtC,kBAAkB,MAAM,oBAAoB;AAAA,IAC5C,QAAQ,OAAO,IAAI,CAAC,MAAM,eAAe,CAAC,CAAC;AAAA,EAC7C;AACF;AAEA,eAAsB,iCACpB,IACA,OACA,UAAkC,CAAC,GACH;AAChC,QAAM,qBAAqB,QAAQ,YAC/B,IAAI,IAAI,QAAQ,UAAU,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,CAAC,IAC/F;AACJ,QAAM,aAAa,uBAAuB,EAAE,OAAO,CAAC,UAAU;AAC5D,QAAI,CAAC,mBAAoB,QAAO;AAChC,WAAO,mBAAmB,IAAI,MAAM,QAAQ;AAAA,EAC9C,CAAC;AACD,QAAM,YAAY,gBAAgB;AAClC,QAAM,gBAAgB,QAAQ,kBAAkB;AAChD,QAAM,SAAS,QAAQ,WAAW;AAClC,QAAM,aAAa,QAAQ,eAAe;AAC1C,QAAM,QAAQ,QAAQ,UAAU;AAChC,QAAM,SAAS,QAAQ;AAEvB,MAAI;AACJ,MAAI,QAAQ,cAAc,QAAW;AACnC,gBAAY,MAAM,KAAK,IAAI,IAAI,QAAQ,UAAU,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC;AAAA,EACzH;AAEA,QAAM,kBAAkB,YAA+B;AACrD,QAAI,cAAc,OAAW,QAAO;AACpC,UAAM,OAAO,MAAM,GAAG,KAAK,QAAQ,EAAE,WAAW,KAAK,GAAU,EAAE,QAAQ,CAAC,IAAI,EAAS,CAAC;AACxF,gBAAY,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE;AACpC,WAAO,aAAa,CAAC;AAAA,EACvB;AAEA,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,MAAI,UAAU;AACd,MAAI,eAAe;AAEnB,aAAW,SAAS,YAAY;AAC9B,UAAM,EAAE,SAAS,IAAI;AACrB,UAAM,OAAO,MAAM;AACnB,UAAM,SAAS,cAAc,MAAM,SAAS;AAC5C,UAAM,WAAW,UAAU,IAAI,QAAQ;AACvC,UAAM,iBAAiB,CAAC,YAAY,CAAC,CAAC;AACtC,UAAM,WAAW,MAAM,WAAW;AAElC,UAAM,SAAyB,CAAC;AAChC,QAAI,UAAU;AACZ,UAAI,cAAe,QAAO,KAAK,EAAE,UAAU,KAAK,CAAC;AAAA,IACnD,OAAO;AACL,YAAM,MAAM,MAAM,gBAAgB;AAClC,UAAI,CAAC,IAAI,QAAQ;AACf;AACA;AAAA,MACF;AACA,iBAAW,YAAY,IAAK,QAAO,KAAK,EAAE,SAAS,CAAC;AAAA,IACtD;AAEA,QAAI,CAAC,OAAO,UAAU,OAAO,WAAW,KAAK,CAAC,gBAAgB;AAC5D;AACA;AAAA,IACF;AAEA,eAAW,SAAS,QAAQ;AAC1B;AACA,YAAM,WAAW,MAAM,WAAW,UAAU,MAAM,QAAQ,KAAK;AAC/D,YAAM,kBAAkB,qBAAqB;AAAA,QAC3C;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AACD,YAAM,WAAW,gBAAgB,eAAe;AAChD,YAAM,WAAW,sBAAsB,QAAQ,IAAI,QAAQ;AAE3D,UAAI,CAAC,UAAU,CAAC,SAAS,OAAO;AAC9B,YAAI;AACF,gBAAM,SAAS,MAAM,MAAM,IAAI,QAAQ;AACvC,cAAI,OAAO,WAAW,YAAY,WAAW,UAAU;AACrD;AACA;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAAC;AAAA,MACX;AAEA,UAAI,eAAyC;AAC7C,UAAI,gBAAgB;AAClB,uBAAe,MAAM,mBAAmB,IAAI,UAAU;AAAA,UACpD,OAAO,MAAM,SAAS;AAAA,UACtB,aAAa,MAAM,eAAe;AAAA,UAClC,gBAAgB;AAAA,UAChB,UAAU,MAAM;AAAA,UAChB,eAAe,MAAM,iBAAiB;AAAA,UACtC,kBAAkB,MAAM,oBAAoB;AAAA,UAC5C,YAAY,MAAM,cAAc;AAAA,UAChC,eAAe,MAAM,iBAAiB;AAAA,UACtC,UAAU;AAAA,UACV;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAEA,UAAI,cAAc,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,EAAE;AACzD,UAAI,OAAO,QAAQ;AACjB,sBAAc,MAAM;AAAA,UAClB;AAAA,UACA,CAAC,EAAE,QAAQ,UAAU,OAAO,CAAC;AAAA,UAC7B,EAAE,gBAAgB,MAAM,UAAU,MAAM,UAAU,QAAQ,WAAW;AAAA,QACvE;AAAA,MACF;AAEA,YAAM,UAAW,iBAAiB,eAAgB,YAAY,UAAU,KAAK,YAAY,UAAU;AACnG,YAAM,8BAA8B,CAAC,WAAW,WAAW;AAC3D,UAAI,6BAA6B;AAC/B,cAAM,2BAA2B,SAAS,QAAW;AAAA,UACnD,UAAU,MAAM;AAAA,UAChB,gBAAgB;AAAA,UAChB,WAAW,CAAC,QAAQ;AAAA,QACtB,CAAC;AAAA,MACH;AAEA,UAAI,SAAS;AACX;AACA,wBAAgB,YAAY,UAAU,YAAY;AAClD,YAAI,CAAC,UAAU,OAAO;AACpB,cAAI;AACF,kBAAM,MAAM,IAAI,UAAU,UAAU,EAAE,MAAM,CAAC,iBAAiB,QAAQ,IAAI,uBAAuB,QAAQ,EAAE,EAAE,CAAC;AAAA,UAChH,QAAQ;AAAA,UAAC;AAAA,QACX;AACA,YAAI,QAAQ;AACV,gBAAM,QAAkB,CAAC;AACzB,cAAI,iBAAiB,YAAa,OAAM,KAAK,UAAU,YAAY,EAAE;AACrE,cAAI,YAAY,WAAW,YAAY,SAAS;AAC9C,kBAAM,KAAK,WAAW,YAAY,OAAO,OAAO,YAAY,OAAO,EAAE;AAAA,UACvE;AACA,iBAAO,UAAU,QAAQ,QAAQ,QAAQ,GAAG,MAAM,SAAS,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,QAC5F;AAAA,MACF,OAAO;AACL;AACA,YAAI,CAAC,UAAU,OAAO;AACpB,cAAI;AACF,kBAAM,MAAM,IAAI,UAAU,UAAU,EAAE,MAAM,CAAC,iBAAiB,QAAQ,IAAI,uBAAuB,QAAQ,EAAE,EAAE,CAAC;AAAA,UAChH,QAAQ;AAAA,UAAC;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,WAAW,cAAc,SAAS,aAAa;AAC1D;AAEO,SAAS,sBAAgC;AAC9C,SAAO,uBAAuB,EAAE,IAAI,CAAC,UAAU,MAAM,QAAQ;AAC/D;AAEO,SAAS,mCAA6D;AAC3E,SAAO,uBAAuB;AAChC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.7-develop.
|
|
3
|
+
"version": "0.6.7-develop.6605.1.761dd4c810",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -254,16 +254,16 @@
|
|
|
254
254
|
"zod": "^4.4.3"
|
|
255
255
|
},
|
|
256
256
|
"peerDependencies": {
|
|
257
|
-
"@open-mercato/ai-assistant": "0.6.7-develop.
|
|
258
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
259
|
-
"@open-mercato/ui": "0.6.7-develop.
|
|
257
|
+
"@open-mercato/ai-assistant": "0.6.7-develop.6605.1.761dd4c810",
|
|
258
|
+
"@open-mercato/shared": "0.6.7-develop.6605.1.761dd4c810",
|
|
259
|
+
"@open-mercato/ui": "0.6.7-develop.6605.1.761dd4c810",
|
|
260
260
|
"react": "^19.0.0",
|
|
261
261
|
"react-dom": "^19.0.0"
|
|
262
262
|
},
|
|
263
263
|
"devDependencies": {
|
|
264
|
-
"@open-mercato/ai-assistant": "0.6.7-develop.
|
|
265
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
266
|
-
"@open-mercato/ui": "0.6.7-develop.
|
|
264
|
+
"@open-mercato/ai-assistant": "0.6.7-develop.6605.1.761dd4c810",
|
|
265
|
+
"@open-mercato/shared": "0.6.7-develop.6605.1.761dd4c810",
|
|
266
|
+
"@open-mercato/ui": "0.6.7-develop.6605.1.761dd4c810",
|
|
267
267
|
"@testing-library/dom": "^10.4.1",
|
|
268
268
|
"@testing-library/jest-dom": "^6.9.1",
|
|
269
269
|
"@testing-library/react": "^16.3.1",
|
|
@@ -49,6 +49,7 @@ const FIELD_DETAIL_KEYS: Array<keyof CustomFieldDefinition> = [
|
|
|
49
49
|
'formEditable',
|
|
50
50
|
'listVisible',
|
|
51
51
|
'indexed',
|
|
52
|
+
'priority',
|
|
52
53
|
'editor',
|
|
53
54
|
'input',
|
|
54
55
|
'relatedEntityId',
|
|
@@ -108,14 +109,28 @@ function buildAggregatedConfigs(): AggregatedEntityConfig[] {
|
|
|
108
109
|
return Array.from(map.values())
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
function resolveFields(fieldSets: CustomFieldSet[]): CustomFieldDefinition[] {
|
|
112
|
+
export function resolveFields(fieldSets: CustomFieldSet[]): CustomFieldDefinition[] {
|
|
112
113
|
const byKey = new Map<string, CustomFieldDefinition>()
|
|
114
|
+
const declarationOrder = new Map<string, number>()
|
|
113
115
|
for (const set of fieldSets) {
|
|
114
116
|
for (const field of set.fields ?? []) {
|
|
117
|
+
if (!declarationOrder.has(field.key)) declarationOrder.set(field.key, declarationOrder.size)
|
|
115
118
|
byKey.set(field.key, { ...field })
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
|
-
|
|
121
|
+
// Declaration order is the author's intent, so it drives `priority` (and the
|
|
122
|
+
// resulting render order) unless a field states its own. Sorting alphabetically
|
|
123
|
+
// by key here would silently discard that intent (#4378).
|
|
124
|
+
const resolved = Array.from(byKey.values()).map((field) => (
|
|
125
|
+
typeof field.priority === 'number'
|
|
126
|
+
? field
|
|
127
|
+
: { ...field, priority: declarationOrder.get(field.key) ?? 0 }
|
|
128
|
+
))
|
|
129
|
+
return resolved.sort((a, b) => {
|
|
130
|
+
const byPriority = (a.priority ?? 0) - (b.priority ?? 0)
|
|
131
|
+
if (byPriority !== 0) return byPriority
|
|
132
|
+
return (declarationOrder.get(a.key) ?? 0) - (declarationOrder.get(b.key) ?? 0)
|
|
133
|
+
})
|
|
119
134
|
}
|
|
120
135
|
|
|
121
136
|
function normalizeField(field: CustomFieldDefinition) {
|