UNPKG

@lucidcms/core

Version:

The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.

1 lines 8.92 kB
{"version":3,"file":"get-document-label.mjs","names":[],"sources":["../../../../src/services/document-publish-operations/helpers/get-document-label.ts"],"sourcesContent":["import type CollectionBuilder from \"../../../libs/collection/builders/collection-builder/index.js\";\nimport type {\n\tCFConfig,\n\tFieldTypes,\n} from \"../../../libs/collection/custom-fields/types.js\";\nimport prefixGeneratedColName from \"../../../libs/collection/helpers/prefix-generated-column-name.js\";\nimport { getDocumentFieldsTableSchema } from \"../../../libs/collection/schema/runtime/runtime-schema-selectors.js\";\nimport type {\n\tAdminCopyInput,\n\tResolvedAdminCopy,\n} from \"../../../libs/i18n/types.js\";\nimport type { DocumentBricksRepository } from \"../../../libs/repositories/index.js\";\nimport type { CollectionTableNames } from \"../../../types.js\";\nimport type {\n\tServiceContext,\n\tServiceResponse,\n} from \"../../../utils/services/types.js\";\n\nconst supportedLabelFieldTypes = [\n\t\"text\",\n\t\"textarea\",\n\t\"select\",\n\t\"number\",\n\t\"datetime\",\n\t\"color\",\n] as const;\n\ntype SupportedLabelFieldType = Extract<\n\t(typeof supportedLabelFieldTypes)[number],\n\tFieldTypes\n>;\ntype LabelFieldConfig = CFConfig<SupportedLabelFieldType>;\n\n/** Reads copy defaults so fallback labels stay useful outside the admin app. */\nconst copyFallback = (\n\tcopy: AdminCopyInput | ResolvedAdminCopy | null | undefined,\n) => {\n\tif (!copy) return null;\n\tif (typeof copy === \"string\") return copy;\n\tif (copy.type === \"lucid.literal\") return copy.value;\n\treturn copy.defaultMessage ?? null;\n};\n\n/** Builds a stable label when no configured label value is available. */\nconst getFallbackLabel = (\n\tcollection: CollectionBuilder,\n\tdocumentId: number,\n) => {\n\tconst collectionData = collection.getData;\n\tconst collectionName =\n\t\tcopyFallback(collectionData.details.singularName) ??\n\t\tcopyFallback(collectionData.details.name) ??\n\t\tcollection.key;\n\n\treturn `${collectionName} #${documentId}`;\n};\n\n/** Keeps publish event labels to direct scalar fields we can read cheaply. */\nconst isSupportedLabelField = (\n\tcollection: CollectionBuilder,\n\tfieldKey: string | null | undefined,\n) => {\n\tif (!fieldKey) return false;\n\n\tconst field = collection.fields.get(fieldKey);\n\tif (!field) return false;\n\tif (field.treeParent !== null || field.structuralParent !== null)\n\t\treturn false;\n\n\treturn supportedLabelFieldTypes.some((type) => type === field.type);\n};\n\n/** Mirrors admin label precedence using only directly stored document fields. */\nconst getLabelField = (collection: CollectionBuilder) => {\n\tfor (const fieldKey of collection.labelFields) {\n\t\tif (isSupportedLabelField(collection, fieldKey)) {\n\t\t\treturn collection.fields.get(fieldKey)?.config as\n\t\t\t\t| LabelFieldConfig\n\t\t\t\t| undefined;\n\t\t}\n\t}\n\n\tfor (const fieldKey of collection.listing) {\n\t\tif (isSupportedLabelField(collection, fieldKey)) {\n\t\t\treturn collection.fields.get(fieldKey)?.config as\n\t\t\t\t| LabelFieldConfig\n\t\t\t\t| undefined;\n\t\t}\n\t}\n\n\tfor (const field of collection.fields.values()) {\n\t\tif (isSupportedLabelField(collection, field.key)) {\n\t\t\treturn field.config as LabelFieldConfig;\n\t\t}\n\t}\n};\n\n/** Converts stored scalar values into compact display labels. */\nconst normalizeScalarLabelValue = (value: unknown) => {\n\tif (typeof value === \"string\") {\n\t\tconst trimmed = value.trim();\n\t\treturn trimmed.length > 0 ? trimmed : null;\n\t}\n\n\tif (typeof value === \"number\" && Number.isFinite(value)) {\n\t\treturn String(value);\n\t}\n\n\tif (value instanceof Date && !Number.isNaN(value.getTime())) {\n\t\treturn value.toISOString();\n\t}\n\n\treturn null;\n};\n\n/** Uses select option copy so stored values render as editor-facing labels. */\nconst formatSelectLabelValue = (\n\tfield: Extract<LabelFieldConfig, { type: \"select\" }>,\n\tvalue: unknown,\n) => {\n\tconst values = Array.isArray(value) ? value : [value];\n\tconst labels = values\n\t\t.map((item) => {\n\t\t\tconst option = field.options.find(\n\t\t\t\t(option) => String(option.value) === String(item),\n\t\t\t);\n\n\t\t\tif (!option) return normalizeScalarLabelValue(item);\n\n\t\t\treturn (\n\t\t\t\tcopyFallback(option.label) ?? normalizeScalarLabelValue(option.value)\n\t\t\t);\n\t\t})\n\t\t.filter((label): label is string => label !== null);\n\n\treturn labels.length > 0 ? labels.join(\", \") : null;\n};\n\n/** Applies field-specific label formatting before falling back. */\nconst normalizeLabelValue = (field: LabelFieldConfig, value: unknown) => {\n\tif (field.type === \"select\") return formatSelectLabelValue(field, value);\n\treturn normalizeScalarLabelValue(value);\n};\n\n/** Resolves the document label snapshot stored on publish operation events. */\nconst getDocumentLabel = async (params: {\n\tcontext: ServiceContext;\n\tbricks: DocumentBricksRepository;\n\tcollection: CollectionBuilder;\n\ttables: CollectionTableNames;\n\toperation: {\n\t\tdocument_id: number;\n\t\tsource_version_id: number;\n\t};\n}): ServiceResponse<string | null> => {\n\tconst labelField = getLabelField(params.collection);\n\tif (!labelField) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: getFallbackLabel(params.collection, params.operation.document_id),\n\t\t};\n\t}\n\n\tconst documentFieldsTableSchemaRes = await getDocumentFieldsTableSchema(\n\t\tparams.context,\n\t\tparams.collection.key,\n\t);\n\tif (documentFieldsTableSchemaRes.error) return documentFieldsTableSchemaRes;\n\tif (!documentFieldsTableSchemaRes.data) {\n\t\treturn {\n\t\t\terror: undefined,\n\t\t\tdata: getFallbackLabel(params.collection, params.operation.document_id),\n\t\t};\n\t}\n\n\tconst fieldsRes = await params.bricks.selectMultipleByVersionId(\n\t\t{\n\t\t\tversionId: params.operation.source_version_id,\n\t\t\tdocumentId: params.operation.document_id,\n\t\t\tbricksSchema: [\n\t\t\t\t{\n\t\t\t\t\tname: params.tables.documentFields,\n\t\t\t\t\tcolumns: documentFieldsTableSchemaRes.data.columns,\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\t{\n\t\t\ttableName: params.tables.version,\n\t\t},\n\t);\n\tif (fieldsRes.error) return fieldsRes;\n\n\tconst columnName = prefixGeneratedColName(labelField.key);\n\tconst documentFields = (fieldsRes.data?.[params.tables.documentFields] ??\n\t\t[]) as Array<Record<string, unknown>>;\n\tconst value = documentFields\n\t\t.map((field) => field[columnName])\n\t\t.map((value) => normalizeLabelValue(labelField, value))\n\t\t.find((value) => value !== null);\n\n\treturn {\n\t\terror: undefined,\n\t\tdata:\n\t\t\tvalue ??\n\t\t\tgetFallbackLabel(params.collection, params.operation.document_id),\n\t};\n};\n\nexport default getDocumentLabel;\n"],"mappings":"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"}