@open-mercato/core 0.6.3-develop.3894.1.352abf4240 → 0.6.3-develop.3901.1.ddad60693a
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.
|
@@ -172,6 +172,38 @@ const ADDRESS_TARGET_OPTIONS = [
|
|
|
172
172
|
function normalizeMatchToken(value) {
|
|
173
173
|
return value.trim().replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
174
174
|
}
|
|
175
|
+
const TRAILING_IMPORT_QUALIFIERS = /* @__PURE__ */ new Set(["external", "imported", "crm"]);
|
|
176
|
+
function addNormalizedMatchToken(tokens, value) {
|
|
177
|
+
const normalized = normalizeMatchToken(value);
|
|
178
|
+
if (normalized.length > 0) {
|
|
179
|
+
tokens.add(normalized);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function stripParentheticalText(value) {
|
|
183
|
+
return value.replace(/\s*\([^)]*\)\s*/g, " ");
|
|
184
|
+
}
|
|
185
|
+
function stripTrailingImportQualifier(value) {
|
|
186
|
+
const parts = normalizeMatchToken(value).split(" ").filter((part) => part.length > 0);
|
|
187
|
+
while (parts.length > 1 && TRAILING_IMPORT_QUALIFIERS.has(parts[parts.length - 1])) {
|
|
188
|
+
parts.pop();
|
|
189
|
+
}
|
|
190
|
+
return parts.join(" ");
|
|
191
|
+
}
|
|
192
|
+
function buildCustomFieldMatchTokens(def, fallback) {
|
|
193
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
194
|
+
const candidates = [
|
|
195
|
+
def.key,
|
|
196
|
+
fallback,
|
|
197
|
+
stripParentheticalText(fallback),
|
|
198
|
+
stripTrailingImportQualifier(def.key),
|
|
199
|
+
stripTrailingImportQualifier(fallback),
|
|
200
|
+
stripTrailingImportQualifier(stripParentheticalText(fallback))
|
|
201
|
+
];
|
|
202
|
+
for (const candidate of candidates) {
|
|
203
|
+
addNormalizedMatchToken(tokens, candidate);
|
|
204
|
+
}
|
|
205
|
+
return Array.from(tokens);
|
|
206
|
+
}
|
|
175
207
|
function titleizeKey(key) {
|
|
176
208
|
return key.split("_").map((part) => part.length > 0 ? part[0].toUpperCase() + part.slice(1) : part).join(" ");
|
|
177
209
|
}
|
|
@@ -207,15 +239,11 @@ function scoreCustomFieldDefinition(def, entityIndex) {
|
|
|
207
239
|
}
|
|
208
240
|
function buildCustomFieldOption(def) {
|
|
209
241
|
const fallback = typeof def.label === "string" && def.label.trim().length > 0 ? def.label.trim() : titleizeKey(def.key);
|
|
210
|
-
const normalizedTokens = Array.from(new Set([
|
|
211
|
-
normalizeMatchToken(def.key),
|
|
212
|
-
normalizeMatchToken(fallback)
|
|
213
|
-
].filter((value) => value.length > 0)));
|
|
214
242
|
return {
|
|
215
243
|
value: `cf:${def.key}`,
|
|
216
244
|
fallback,
|
|
217
245
|
mappingKind: "custom_field",
|
|
218
|
-
matchTokens:
|
|
246
|
+
matchTokens: buildCustomFieldMatchTokens(def, fallback)
|
|
219
247
|
};
|
|
220
248
|
}
|
|
221
249
|
function selectPreferredCustomFieldDefs(customFieldDefs) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/sync_excel/widgets/injection/upload-config/target-options.ts"],
|
|
4
|
-
"sourcesContent": ["import type { CustomFieldDefDto } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport type { FieldMapping, FieldMappingDedupeRole, FieldMappingKind } from '../../../../data_sync/lib/adapter'\n\nexport const SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS = [\n 'customers:customer_entity',\n 'customers:customer_person_profile',\n] as const\n\nexport type SuggestedMapping = {\n entityType: 'customers.person'\n matchStrategy: 'externalId' | 'email' | 'custom'\n matchField?: string\n fields: FieldMapping[]\n unmappedColumns: string[]\n}\n\nexport type MappingTargetOption = {\n value: string\n labelKey?: string\n fallback: string\n mappingKind: FieldMappingKind\n dedupeRole?: FieldMappingDedupeRole\n matchTokens: string[]\n}\n\nconst CORE_TARGET_OPTIONS: MappingTargetOption[] = [\n {\n value: 'person.externalId',\n labelKey: 'sync_excel.mapping.targets.externalId',\n fallback: 'External ID',\n mappingKind: 'external_id',\n dedupeRole: 'primary',\n matchTokens: ['external id', 'record id', 'lead id'],\n },\n {\n value: 'person.firstName',\n labelKey: 'sync_excel.mapping.targets.firstName',\n fallback: 'First name',\n mappingKind: 'core',\n matchTokens: ['first name', 'firstname', 'given name'],\n },\n {\n value: 'person.lastName',\n labelKey: 'sync_excel.mapping.targets.lastName',\n fallback: 'Last name',\n mappingKind: 'core',\n matchTokens: ['last name', 'lastname', 'surname', 'family name'],\n },\n {\n value: 'person.displayName',\n labelKey: 'sync_excel.mapping.targets.displayName',\n fallback: 'Display name',\n mappingKind: 'core',\n matchTokens: ['display name', 'lead name', 'full name', 'name'],\n },\n {\n value: 'person.primaryEmail',\n labelKey: 'sync_excel.mapping.targets.primaryEmail',\n fallback: 'Primary email',\n mappingKind: 'core',\n dedupeRole: 'secondary',\n matchTokens: ['email', 'primary email', 'email address'],\n },\n {\n value: 'person.primaryPhone',\n labelKey: 'sync_excel.mapping.targets.primaryPhone',\n fallback: 'Primary phone',\n mappingKind: 'core',\n matchTokens: ['phone', 'primary phone', 'mobile', 'mobile phone', 'telephone'],\n },\n {\n value: 'person.jobTitle',\n labelKey: 'sync_excel.mapping.targets.jobTitle',\n fallback: 'Job title',\n mappingKind: 'core',\n matchTokens: ['job title', 'title', 'position'],\n },\n {\n value: 'person.status',\n labelKey: 'sync_excel.mapping.targets.status',\n fallback: 'Status',\n mappingKind: 'core',\n matchTokens: ['status', 'lead status'],\n },\n {\n value: 'person.source',\n labelKey: 'sync_excel.mapping.targets.source',\n fallback: 'Source',\n mappingKind: 'core',\n matchTokens: ['source', 'lead source'],\n },\n {\n value: 'person.description',\n labelKey: 'sync_excel.mapping.targets.description',\n fallback: 'Description',\n mappingKind: 'core',\n matchTokens: ['description', 'notes', 'comment'],\n },\n]\n\nconst ADDRESS_TARGET_OPTIONS: MappingTargetOption[] = [\n {\n value: 'address.name',\n labelKey: 'sync_excel.mapping.targets.addressName',\n fallback: 'Address label',\n mappingKind: 'core',\n matchTokens: ['address label', 'address name', 'label'],\n },\n {\n value: 'address.purpose',\n labelKey: 'sync_excel.mapping.targets.addressPurpose',\n fallback: 'Address purpose',\n mappingKind: 'core',\n matchTokens: ['address purpose', 'address type'],\n },\n {\n value: 'address.companyName',\n labelKey: 'sync_excel.mapping.targets.addressCompanyName',\n fallback: 'Address company',\n mappingKind: 'core',\n matchTokens: ['address company', 'address company name'],\n },\n {\n value: 'address.addressLine1',\n labelKey: 'sync_excel.mapping.targets.addressLine1',\n fallback: 'Address line 1',\n mappingKind: 'core',\n matchTokens: ['address line 1', 'street address', 'street', 'street 1'],\n },\n {\n value: 'address.addressLine2',\n labelKey: 'sync_excel.mapping.targets.addressLine2',\n fallback: 'Address line 2',\n mappingKind: 'core',\n matchTokens: ['address line 2', 'street 2'],\n },\n {\n value: 'address.buildingNumber',\n labelKey: 'sync_excel.mapping.targets.buildingNumber',\n fallback: 'Building number',\n mappingKind: 'core',\n matchTokens: ['building number', 'building no', 'house number'],\n },\n {\n value: 'address.flatNumber',\n labelKey: 'sync_excel.mapping.targets.flatNumber',\n fallback: 'Flat number',\n mappingKind: 'core',\n matchTokens: ['flat number', 'apartment number', 'unit number'],\n },\n {\n value: 'address.city',\n labelKey: 'sync_excel.mapping.targets.city',\n fallback: 'City',\n mappingKind: 'core',\n matchTokens: ['city', 'town'],\n },\n {\n value: 'address.region',\n labelKey: 'sync_excel.mapping.targets.region',\n fallback: 'Region / State',\n mappingKind: 'core',\n matchTokens: ['region', 'state', 'province'],\n },\n {\n value: 'address.postalCode',\n labelKey: 'sync_excel.mapping.targets.postalCode',\n fallback: 'Postal code',\n mappingKind: 'core',\n matchTokens: ['postal code', 'zip code', 'postcode'],\n },\n {\n value: 'address.country',\n labelKey: 'sync_excel.mapping.targets.country',\n fallback: 'Country',\n mappingKind: 'core',\n matchTokens: ['country'],\n },\n {\n value: 'address.latitude',\n labelKey: 'sync_excel.mapping.targets.latitude',\n fallback: 'Latitude',\n mappingKind: 'core',\n matchTokens: ['latitude', 'lat'],\n },\n {\n value: 'address.longitude',\n labelKey: 'sync_excel.mapping.targets.longitude',\n fallback: 'Longitude',\n mappingKind: 'core',\n matchTokens: ['longitude', 'lng', 'lon'],\n },\n]\n\nfunction normalizeMatchToken(value: string): string {\n return value\n .trim()\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .replace(/[_-]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction titleizeKey(key: string): string {\n return key\n .split('_')\n .map((part) => (part.length > 0 ? part[0].toUpperCase() + part.slice(1) : part))\n .join(' ')\n}\n\nfunction scoreCustomFieldDefinition(def: CustomFieldDefDto, entityIndex: number): {\n base: number\n penalty: number\n entityIndex: number\n} {\n const listVisibleScore = def.listVisible === false ? 0 : 1\n const formEditableScore = def.formEditable === false ? 0 : 1\n const filterableScore = def.filterable ? 1 : 0\n const kindScore = (() => {\n switch (def.kind) {\n case 'dictionary':\n return 8\n case 'relation':\n return 6\n case 'select':\n return 4\n case 'multiline':\n return 3\n case 'boolean':\n case 'integer':\n case 'float':\n return 2\n default:\n return 1\n }\n })()\n const optionsBonus = Array.isArray(def.options) && def.options.length > 0 ? 2 : 0\n const dictionaryBonus = typeof def.dictionaryId === 'string' && def.dictionaryId.trim().length > 0 ? 5 : 0\n return {\n base: (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus,\n penalty: typeof def.priority === 'number' ? def.priority : 0,\n entityIndex,\n }\n}\n\nfunction buildCustomFieldOption(def: CustomFieldDefDto): MappingTargetOption {\n const fallback = typeof def.label === 'string' && def.label.trim().length > 0\n ? def.label.trim()\n : titleizeKey(def.key)\n const normalizedTokens = Array.from(new Set([\n normalizeMatchToken(def.key),\n normalizeMatchToken(fallback),\n ].filter((value) => value.length > 0)))\n\n return {\n value: `cf:${def.key}`,\n fallback,\n mappingKind: 'custom_field',\n matchTokens: normalizedTokens,\n }\n}\n\nfunction selectPreferredCustomFieldDefs(customFieldDefs: CustomFieldDefDto[]): CustomFieldDefDto[] {\n const entityOrder = new Map<string, number>()\n SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS.forEach((entityId, index) => entityOrder.set(entityId, index))\n const bestByKey = new Map<string, { def: CustomFieldDefDto; score: { base: number; penalty: number; entityIndex: number } }>()\n\n for (const def of customFieldDefs) {\n if (typeof def.key !== 'string' || def.key.trim().length === 0) continue\n const score = scoreCustomFieldDefinition(def, entityOrder.get(def.entityId ?? '') ?? Number.MAX_SAFE_INTEGER)\n const existing = bestByKey.get(def.key)\n const isBetter = !existing\n || score.base > existing.score.base\n || (\n score.base === existing.score.base\n && (score.penalty < existing.score.penalty\n || (score.penalty === existing.score.penalty && score.entityIndex < existing.score.entityIndex))\n )\n if (isBetter) {\n bestByKey.set(def.key, { def, score })\n }\n }\n\n return Array.from(bestByKey.values()).map((entry) => entry.def)\n}\n\nexport function buildPeopleTargetOptions(customFieldDefs: CustomFieldDefDto[]): MappingTargetOption[] {\n const customOptions = selectPreferredCustomFieldDefs(customFieldDefs).map(buildCustomFieldOption)\n return [...CORE_TARGET_OPTIONS, ...ADDRESS_TARGET_OPTIONS, ...customOptions]\n}\n\nexport function buildPeopleSuggestedMapping(\n headers: string[],\n suggestedMapping: SuggestedMapping,\n customFieldDefs: CustomFieldDefDto[],\n): SuggestedMapping {\n const fields = [...suggestedMapping.fields]\n const usedExternalFields = new Set(fields.map((field) => field.externalField))\n const usedTargetFields = new Set(fields.map((field) => field.localField))\n const supplementalTargetOptions = buildPeopleTargetOptions(customFieldDefs).filter(\n (option) => option.mappingKind === 'custom_field' || option.value.startsWith('address.'),\n )\n\n for (const header of headers) {\n if (usedExternalFields.has(header)) continue\n const normalizedHeader = normalizeMatchToken(header)\n const matchedOption = supplementalTargetOptions.find((option) => {\n if (usedTargetFields.has(option.value)) return false\n return option.matchTokens.includes(normalizedHeader)\n })\n if (!matchedOption) continue\n\n fields.push({\n externalField: header,\n localField: matchedOption.value,\n mappingKind: matchedOption.mappingKind,\n })\n usedExternalFields.add(header)\n usedTargetFields.add(matchedOption.value)\n }\n\n return {\n ...suggestedMapping,\n fields,\n unmappedColumns: headers.filter((header) => !usedExternalFields.has(header)),\n }\n}\n\nexport function buildSuggestedMappingSignature(headers: string[], suggestedMapping: SuggestedMapping): string {\n return JSON.stringify({\n headers,\n matchStrategy: suggestedMapping.matchStrategy,\n matchField: suggestedMapping.matchField ?? null,\n fields: suggestedMapping.fields.map((field) => ({\n externalField: field.externalField,\n localField: field.localField,\n mappingKind: field.mappingKind ?? null,\n dedupeRole: field.dedupeRole ?? null,\n })),\n })\n}\n\nexport function findMappingTargetOption(\n targetOptions: MappingTargetOption[],\n targetField: string,\n): MappingTargetOption | undefined {\n return targetOptions.find((option) => option.value === targetField)\n}\n\nexport { normalizeMatchToken }\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type { CustomFieldDefDto } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport type { FieldMapping, FieldMappingDedupeRole, FieldMappingKind } from '../../../../data_sync/lib/adapter'\n\nexport const SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS = [\n 'customers:customer_entity',\n 'customers:customer_person_profile',\n] as const\n\nexport type SuggestedMapping = {\n entityType: 'customers.person'\n matchStrategy: 'externalId' | 'email' | 'custom'\n matchField?: string\n fields: FieldMapping[]\n unmappedColumns: string[]\n}\n\nexport type MappingTargetOption = {\n value: string\n labelKey?: string\n fallback: string\n mappingKind: FieldMappingKind\n dedupeRole?: FieldMappingDedupeRole\n matchTokens: string[]\n}\n\nconst CORE_TARGET_OPTIONS: MappingTargetOption[] = [\n {\n value: 'person.externalId',\n labelKey: 'sync_excel.mapping.targets.externalId',\n fallback: 'External ID',\n mappingKind: 'external_id',\n dedupeRole: 'primary',\n matchTokens: ['external id', 'record id', 'lead id'],\n },\n {\n value: 'person.firstName',\n labelKey: 'sync_excel.mapping.targets.firstName',\n fallback: 'First name',\n mappingKind: 'core',\n matchTokens: ['first name', 'firstname', 'given name'],\n },\n {\n value: 'person.lastName',\n labelKey: 'sync_excel.mapping.targets.lastName',\n fallback: 'Last name',\n mappingKind: 'core',\n matchTokens: ['last name', 'lastname', 'surname', 'family name'],\n },\n {\n value: 'person.displayName',\n labelKey: 'sync_excel.mapping.targets.displayName',\n fallback: 'Display name',\n mappingKind: 'core',\n matchTokens: ['display name', 'lead name', 'full name', 'name'],\n },\n {\n value: 'person.primaryEmail',\n labelKey: 'sync_excel.mapping.targets.primaryEmail',\n fallback: 'Primary email',\n mappingKind: 'core',\n dedupeRole: 'secondary',\n matchTokens: ['email', 'primary email', 'email address'],\n },\n {\n value: 'person.primaryPhone',\n labelKey: 'sync_excel.mapping.targets.primaryPhone',\n fallback: 'Primary phone',\n mappingKind: 'core',\n matchTokens: ['phone', 'primary phone', 'mobile', 'mobile phone', 'telephone'],\n },\n {\n value: 'person.jobTitle',\n labelKey: 'sync_excel.mapping.targets.jobTitle',\n fallback: 'Job title',\n mappingKind: 'core',\n matchTokens: ['job title', 'title', 'position'],\n },\n {\n value: 'person.status',\n labelKey: 'sync_excel.mapping.targets.status',\n fallback: 'Status',\n mappingKind: 'core',\n matchTokens: ['status', 'lead status'],\n },\n {\n value: 'person.source',\n labelKey: 'sync_excel.mapping.targets.source',\n fallback: 'Source',\n mappingKind: 'core',\n matchTokens: ['source', 'lead source'],\n },\n {\n value: 'person.description',\n labelKey: 'sync_excel.mapping.targets.description',\n fallback: 'Description',\n mappingKind: 'core',\n matchTokens: ['description', 'notes', 'comment'],\n },\n]\n\nconst ADDRESS_TARGET_OPTIONS: MappingTargetOption[] = [\n {\n value: 'address.name',\n labelKey: 'sync_excel.mapping.targets.addressName',\n fallback: 'Address label',\n mappingKind: 'core',\n matchTokens: ['address label', 'address name', 'label'],\n },\n {\n value: 'address.purpose',\n labelKey: 'sync_excel.mapping.targets.addressPurpose',\n fallback: 'Address purpose',\n mappingKind: 'core',\n matchTokens: ['address purpose', 'address type'],\n },\n {\n value: 'address.companyName',\n labelKey: 'sync_excel.mapping.targets.addressCompanyName',\n fallback: 'Address company',\n mappingKind: 'core',\n matchTokens: ['address company', 'address company name'],\n },\n {\n value: 'address.addressLine1',\n labelKey: 'sync_excel.mapping.targets.addressLine1',\n fallback: 'Address line 1',\n mappingKind: 'core',\n matchTokens: ['address line 1', 'street address', 'street', 'street 1'],\n },\n {\n value: 'address.addressLine2',\n labelKey: 'sync_excel.mapping.targets.addressLine2',\n fallback: 'Address line 2',\n mappingKind: 'core',\n matchTokens: ['address line 2', 'street 2'],\n },\n {\n value: 'address.buildingNumber',\n labelKey: 'sync_excel.mapping.targets.buildingNumber',\n fallback: 'Building number',\n mappingKind: 'core',\n matchTokens: ['building number', 'building no', 'house number'],\n },\n {\n value: 'address.flatNumber',\n labelKey: 'sync_excel.mapping.targets.flatNumber',\n fallback: 'Flat number',\n mappingKind: 'core',\n matchTokens: ['flat number', 'apartment number', 'unit number'],\n },\n {\n value: 'address.city',\n labelKey: 'sync_excel.mapping.targets.city',\n fallback: 'City',\n mappingKind: 'core',\n matchTokens: ['city', 'town'],\n },\n {\n value: 'address.region',\n labelKey: 'sync_excel.mapping.targets.region',\n fallback: 'Region / State',\n mappingKind: 'core',\n matchTokens: ['region', 'state', 'province'],\n },\n {\n value: 'address.postalCode',\n labelKey: 'sync_excel.mapping.targets.postalCode',\n fallback: 'Postal code',\n mappingKind: 'core',\n matchTokens: ['postal code', 'zip code', 'postcode'],\n },\n {\n value: 'address.country',\n labelKey: 'sync_excel.mapping.targets.country',\n fallback: 'Country',\n mappingKind: 'core',\n matchTokens: ['country'],\n },\n {\n value: 'address.latitude',\n labelKey: 'sync_excel.mapping.targets.latitude',\n fallback: 'Latitude',\n mappingKind: 'core',\n matchTokens: ['latitude', 'lat'],\n },\n {\n value: 'address.longitude',\n labelKey: 'sync_excel.mapping.targets.longitude',\n fallback: 'Longitude',\n mappingKind: 'core',\n matchTokens: ['longitude', 'lng', 'lon'],\n },\n]\n\nfunction normalizeMatchToken(value: string): string {\n return value\n .trim()\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .replace(/[_-]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nconst TRAILING_IMPORT_QUALIFIERS = new Set(['external', 'imported', 'crm'])\n\nfunction addNormalizedMatchToken(tokens: Set<string>, value: string): void {\n const normalized = normalizeMatchToken(value)\n if (normalized.length > 0) {\n tokens.add(normalized)\n }\n}\n\nfunction stripParentheticalText(value: string): string {\n return value.replace(/\\s*\\([^)]*\\)\\s*/g, ' ')\n}\n\nfunction stripTrailingImportQualifier(value: string): string {\n const parts = normalizeMatchToken(value).split(' ').filter((part) => part.length > 0)\n while (parts.length > 1 && TRAILING_IMPORT_QUALIFIERS.has(parts[parts.length - 1])) {\n parts.pop()\n }\n return parts.join(' ')\n}\n\nfunction buildCustomFieldMatchTokens(def: CustomFieldDefDto, fallback: string): string[] {\n const tokens = new Set<string>()\n const candidates = [\n def.key,\n fallback,\n stripParentheticalText(fallback),\n stripTrailingImportQualifier(def.key),\n stripTrailingImportQualifier(fallback),\n stripTrailingImportQualifier(stripParentheticalText(fallback)),\n ]\n\n for (const candidate of candidates) {\n addNormalizedMatchToken(tokens, candidate)\n }\n\n return Array.from(tokens)\n}\n\nfunction titleizeKey(key: string): string {\n return key\n .split('_')\n .map((part) => (part.length > 0 ? part[0].toUpperCase() + part.slice(1) : part))\n .join(' ')\n}\n\nfunction scoreCustomFieldDefinition(def: CustomFieldDefDto, entityIndex: number): {\n base: number\n penalty: number\n entityIndex: number\n} {\n const listVisibleScore = def.listVisible === false ? 0 : 1\n const formEditableScore = def.formEditable === false ? 0 : 1\n const filterableScore = def.filterable ? 1 : 0\n const kindScore = (() => {\n switch (def.kind) {\n case 'dictionary':\n return 8\n case 'relation':\n return 6\n case 'select':\n return 4\n case 'multiline':\n return 3\n case 'boolean':\n case 'integer':\n case 'float':\n return 2\n default:\n return 1\n }\n })()\n const optionsBonus = Array.isArray(def.options) && def.options.length > 0 ? 2 : 0\n const dictionaryBonus = typeof def.dictionaryId === 'string' && def.dictionaryId.trim().length > 0 ? 5 : 0\n return {\n base: (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus,\n penalty: typeof def.priority === 'number' ? def.priority : 0,\n entityIndex,\n }\n}\n\nfunction buildCustomFieldOption(def: CustomFieldDefDto): MappingTargetOption {\n const fallback = typeof def.label === 'string' && def.label.trim().length > 0\n ? def.label.trim()\n : titleizeKey(def.key)\n\n return {\n value: `cf:${def.key}`,\n fallback,\n mappingKind: 'custom_field',\n matchTokens: buildCustomFieldMatchTokens(def, fallback),\n }\n}\n\nfunction selectPreferredCustomFieldDefs(customFieldDefs: CustomFieldDefDto[]): CustomFieldDefDto[] {\n const entityOrder = new Map<string, number>()\n SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS.forEach((entityId, index) => entityOrder.set(entityId, index))\n const bestByKey = new Map<string, { def: CustomFieldDefDto; score: { base: number; penalty: number; entityIndex: number } }>()\n\n for (const def of customFieldDefs) {\n if (typeof def.key !== 'string' || def.key.trim().length === 0) continue\n const score = scoreCustomFieldDefinition(def, entityOrder.get(def.entityId ?? '') ?? Number.MAX_SAFE_INTEGER)\n const existing = bestByKey.get(def.key)\n const isBetter = !existing\n || score.base > existing.score.base\n || (\n score.base === existing.score.base\n && (score.penalty < existing.score.penalty\n || (score.penalty === existing.score.penalty && score.entityIndex < existing.score.entityIndex))\n )\n if (isBetter) {\n bestByKey.set(def.key, { def, score })\n }\n }\n\n return Array.from(bestByKey.values()).map((entry) => entry.def)\n}\n\nexport function buildPeopleTargetOptions(customFieldDefs: CustomFieldDefDto[]): MappingTargetOption[] {\n const customOptions = selectPreferredCustomFieldDefs(customFieldDefs).map(buildCustomFieldOption)\n return [...CORE_TARGET_OPTIONS, ...ADDRESS_TARGET_OPTIONS, ...customOptions]\n}\n\nexport function buildPeopleSuggestedMapping(\n headers: string[],\n suggestedMapping: SuggestedMapping,\n customFieldDefs: CustomFieldDefDto[],\n): SuggestedMapping {\n const fields = [...suggestedMapping.fields]\n const usedExternalFields = new Set(fields.map((field) => field.externalField))\n const usedTargetFields = new Set(fields.map((field) => field.localField))\n const supplementalTargetOptions = buildPeopleTargetOptions(customFieldDefs).filter(\n (option) => option.mappingKind === 'custom_field' || option.value.startsWith('address.'),\n )\n\n for (const header of headers) {\n if (usedExternalFields.has(header)) continue\n const normalizedHeader = normalizeMatchToken(header)\n const matchedOption = supplementalTargetOptions.find((option) => {\n if (usedTargetFields.has(option.value)) return false\n return option.matchTokens.includes(normalizedHeader)\n })\n if (!matchedOption) continue\n\n fields.push({\n externalField: header,\n localField: matchedOption.value,\n mappingKind: matchedOption.mappingKind,\n })\n usedExternalFields.add(header)\n usedTargetFields.add(matchedOption.value)\n }\n\n return {\n ...suggestedMapping,\n fields,\n unmappedColumns: headers.filter((header) => !usedExternalFields.has(header)),\n }\n}\n\nexport function buildSuggestedMappingSignature(headers: string[], suggestedMapping: SuggestedMapping): string {\n return JSON.stringify({\n headers,\n matchStrategy: suggestedMapping.matchStrategy,\n matchField: suggestedMapping.matchField ?? null,\n fields: suggestedMapping.fields.map((field) => ({\n externalField: field.externalField,\n localField: field.localField,\n mappingKind: field.mappingKind ?? null,\n dedupeRole: field.dedupeRole ?? null,\n })),\n })\n}\n\nexport function findMappingTargetOption(\n targetOptions: MappingTargetOption[],\n targetField: string,\n): MappingTargetOption | undefined {\n return targetOptions.find((option) => option.value === targetField)\n}\n\nexport { normalizeMatchToken }\n"],
|
|
5
|
+
"mappings": "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",
|
|
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.3-develop.
|
|
3
|
+
"version": "0.6.3-develop.3901.1.ddad60693a",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -243,16 +243,16 @@
|
|
|
243
243
|
"zod": "^4.4.3"
|
|
244
244
|
},
|
|
245
245
|
"peerDependencies": {
|
|
246
|
-
"@open-mercato/ai-assistant": "0.6.3-develop.
|
|
247
|
-
"@open-mercato/shared": "0.6.3-develop.
|
|
248
|
-
"@open-mercato/ui": "0.6.3-develop.
|
|
246
|
+
"@open-mercato/ai-assistant": "0.6.3-develop.3901.1.ddad60693a",
|
|
247
|
+
"@open-mercato/shared": "0.6.3-develop.3901.1.ddad60693a",
|
|
248
|
+
"@open-mercato/ui": "0.6.3-develop.3901.1.ddad60693a",
|
|
249
249
|
"react": "^19.0.0",
|
|
250
250
|
"react-dom": "^19.0.0"
|
|
251
251
|
},
|
|
252
252
|
"devDependencies": {
|
|
253
|
-
"@open-mercato/ai-assistant": "0.6.3-develop.
|
|
254
|
-
"@open-mercato/shared": "0.6.3-develop.
|
|
255
|
-
"@open-mercato/ui": "0.6.3-develop.
|
|
253
|
+
"@open-mercato/ai-assistant": "0.6.3-develop.3901.1.ddad60693a",
|
|
254
|
+
"@open-mercato/shared": "0.6.3-develop.3901.1.ddad60693a",
|
|
255
|
+
"@open-mercato/ui": "0.6.3-develop.3901.1.ddad60693a",
|
|
256
256
|
"@testing-library/dom": "^10.4.1",
|
|
257
257
|
"@testing-library/jest-dom": "^6.9.1",
|
|
258
258
|
"@testing-library/react": "^16.3.1",
|
|
@@ -202,6 +202,45 @@ function normalizeMatchToken(value: string): string {
|
|
|
202
202
|
.trim()
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
const TRAILING_IMPORT_QUALIFIERS = new Set(['external', 'imported', 'crm'])
|
|
206
|
+
|
|
207
|
+
function addNormalizedMatchToken(tokens: Set<string>, value: string): void {
|
|
208
|
+
const normalized = normalizeMatchToken(value)
|
|
209
|
+
if (normalized.length > 0) {
|
|
210
|
+
tokens.add(normalized)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function stripParentheticalText(value: string): string {
|
|
215
|
+
return value.replace(/\s*\([^)]*\)\s*/g, ' ')
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function stripTrailingImportQualifier(value: string): string {
|
|
219
|
+
const parts = normalizeMatchToken(value).split(' ').filter((part) => part.length > 0)
|
|
220
|
+
while (parts.length > 1 && TRAILING_IMPORT_QUALIFIERS.has(parts[parts.length - 1])) {
|
|
221
|
+
parts.pop()
|
|
222
|
+
}
|
|
223
|
+
return parts.join(' ')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function buildCustomFieldMatchTokens(def: CustomFieldDefDto, fallback: string): string[] {
|
|
227
|
+
const tokens = new Set<string>()
|
|
228
|
+
const candidates = [
|
|
229
|
+
def.key,
|
|
230
|
+
fallback,
|
|
231
|
+
stripParentheticalText(fallback),
|
|
232
|
+
stripTrailingImportQualifier(def.key),
|
|
233
|
+
stripTrailingImportQualifier(fallback),
|
|
234
|
+
stripTrailingImportQualifier(stripParentheticalText(fallback)),
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
for (const candidate of candidates) {
|
|
238
|
+
addNormalizedMatchToken(tokens, candidate)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return Array.from(tokens)
|
|
242
|
+
}
|
|
243
|
+
|
|
205
244
|
function titleizeKey(key: string): string {
|
|
206
245
|
return key
|
|
207
246
|
.split('_')
|
|
@@ -248,16 +287,12 @@ function buildCustomFieldOption(def: CustomFieldDefDto): MappingTargetOption {
|
|
|
248
287
|
const fallback = typeof def.label === 'string' && def.label.trim().length > 0
|
|
249
288
|
? def.label.trim()
|
|
250
289
|
: titleizeKey(def.key)
|
|
251
|
-
const normalizedTokens = Array.from(new Set([
|
|
252
|
-
normalizeMatchToken(def.key),
|
|
253
|
-
normalizeMatchToken(fallback),
|
|
254
|
-
].filter((value) => value.length > 0)))
|
|
255
290
|
|
|
256
291
|
return {
|
|
257
292
|
value: `cf:${def.key}`,
|
|
258
293
|
fallback,
|
|
259
294
|
mappingKind: 'custom_field',
|
|
260
|
-
matchTokens:
|
|
295
|
+
matchTokens: buildCustomFieldMatchTokens(def, fallback),
|
|
261
296
|
}
|
|
262
297
|
}
|
|
263
298
|
|