@lucidcms/core
Version:
The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.
1 lines • 8.6 kB
Source Map (JSON)
{"version":3,"file":"fetch-validation-data.mjs","names":[],"sources":["../../../../src/services/documents-bricks/helpers/fetch-validation-data.ts"],"sourcesContent":["import type BrickBuilder from \"../../../libs/collection/builders/brick-builder/index.js\";\nimport type CollectionBuilder from \"../../../libs/collection/builders/collection-builder/index.js\";\nimport registeredFields, {\n\tregisteredFieldTypes,\n} from \"../../../libs/collection/custom-fields/registered-fields.js\";\nimport { isStorageMode } from \"../../../libs/collection/custom-fields/storage/index.js\";\nimport type {\n\tFieldRelationValidationInput,\n\tFieldTypes,\n} from \"../../../libs/collection/custom-fields/types.js\";\nimport type { BrickInputSchema } from \"../../../schemas/collection-bricks.js\";\nimport type { FieldInputSchema } from \"../../../types.js\";\nimport type {\n\tServiceContext,\n\tServiceFn,\n} from \"../../../utils/services/types.js\";\n\nexport type ValidationData = Partial<Record<FieldTypes, unknown>>;\n\ntype ValidationBuckets = Partial<\n\tRecord<FieldTypes, Record<string, Set<number>>>\n>;\n\n/**\n * Merges a field instance's grouped validation IDs into the shared buckets.\n */\nconst mergeValidationInput = (\n\tfieldType: FieldTypes,\n\tinput: FieldRelationValidationInput,\n\tbuckets: ValidationBuckets,\n) => {\n\tconst fieldBuckets = buckets[fieldType] ?? {};\n\n\tfor (const groupKey in input) {\n\t\tconst groupIds = input[groupKey];\n\t\tif (!groupIds) continue;\n\n\t\tconst groupBucket = fieldBuckets[groupKey] ?? new Set<number>();\n\t\tfor (const id of groupIds) {\n\t\t\tgroupBucket.add(id);\n\t\t}\n\t\tfieldBuckets[groupKey] = groupBucket;\n\t}\n\n\tbuckets[fieldType] = fieldBuckets;\n};\n\n/**\n * Collects grouped validation IDs from a field value and any translations.\n */\nconst collectFieldValidationInput = (\n\tfield: FieldInputSchema,\n\tinstance: CollectionBuilder | BrickBuilder,\n): FieldRelationValidationInput | null => {\n\tconst fieldInstance = instance.fields.get(field.key);\n\tif (!fieldInstance) return null;\n\n\tconst mergedInput: FieldRelationValidationInput = {};\n\n\tconst pushValue = (value: unknown) => {\n\t\tconst validationInput =\n\t\t\tfieldInstance.getRelationFieldValidationInput(value);\n\n\t\tfor (const groupKey in validationInput) {\n\t\t\tconst groupIds = validationInput[groupKey];\n\t\t\tif (!groupIds) continue;\n\n\t\t\tconst currentIds = mergedInput[groupKey] ?? [];\n\t\t\tcurrentIds.push(...groupIds);\n\t\t\tmergedInput[groupKey] = currentIds;\n\t\t}\n\t};\n\n\tif (field.value !== undefined && field.value !== null) {\n\t\tpushValue(field.value);\n\t}\n\n\tif (field.translations) {\n\t\tfor (const localeCode in field.translations) {\n\t\t\tconst localeValue = field.translations[localeCode];\n\t\t\tif (localeValue === undefined || localeValue === null) continue;\n\n\t\t\tpushValue(localeValue);\n\t\t}\n\t}\n\n\treturn mergedInput;\n};\n\n/**\n * Responsible for fetching data used for validating custom field values.\n */\nconst fetchValidationData: ServiceFn<\n\t[\n\t\t{\n\t\t\tbricks: Array<BrickInputSchema>;\n\t\t\tfields: Array<FieldInputSchema>;\n\t\t\tcollection: CollectionBuilder;\n\t\t},\n\t],\n\tValidationData\n> = async (context, data) => {\n\tconst buckets = extractRelationIds(data.bricks, data.fields, data.collection);\n\tconst validationData = await buildValidationData(context, buckets);\n\n\treturn {\n\t\tdata: validationData,\n\t\terror: undefined,\n\t};\n};\n\n/**\n * Extracts grouped relation IDs from the provided bricks and fields.\n */\nconst extractRelationIds = (\n\tbricks: Array<BrickInputSchema>,\n\tfields: Array<FieldInputSchema>,\n\tcollection: CollectionBuilder,\n): ValidationBuckets => {\n\tconst buckets: ValidationBuckets = {};\n\n\tfor (const brick of bricks) {\n\t\tconst instance = getBrickInstance(brick, collection);\n\t\tif (!instance || !brick.fields) continue;\n\n\t\textractRelationIdsFromFields(brick.fields, instance, buckets);\n\t}\n\n\textractRelationIdsFromFields(fields, collection, buckets);\n\treturn buckets;\n};\n\n/**\n * Returns the configured brick instance for a submitted brick payload.\n */\nconst getBrickInstance = (\n\tbrick: BrickInputSchema,\n\tcollection: CollectionBuilder,\n): CollectionBuilder | BrickBuilder | undefined => {\n\tswitch (brick.type) {\n\t\tcase \"builder\":\n\t\t\treturn collection.config.bricks?.builder?.find(\n\t\t\t\t(b) => b.key === brick.key,\n\t\t\t);\n\t\tcase \"fixed\":\n\t\t\treturn collection.config.bricks?.fixed?.find((b) => b.key === brick.key);\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n};\n\n/**\n * Recursively extracts grouped relation IDs from a field tree.\n */\nconst extractRelationIdsFromFields = (\n\tfields: Array<FieldInputSchema>,\n\tinstance: CollectionBuilder | BrickBuilder,\n\tbuckets: ValidationBuckets,\n) => {\n\tfor (const field of fields) {\n\t\tconst fieldInstance = instance.fields.get(field.key);\n\t\tif (!fieldInstance) continue;\n\n\t\tconst fieldDefinition = registeredFields[fieldInstance.type];\n\t\tconst databaseConfig = fieldDefinition.config.database;\n\n\t\tif (fieldDefinition.validateInput !== null) {\n\t\t\tconst validationInput = collectFieldValidationInput(field, instance);\n\t\t\tif (validationInput) {\n\t\t\t\tmergeValidationInput(field.type, validationInput, buckets);\n\t\t\t}\n\t\t}\n\n\t\tif (isStorageMode(databaseConfig, \"tree-table\") && field.groups) {\n\t\t\tfor (const group of field.groups) {\n\t\t\t\textractRelationIdsFromFields(group.fields, instance, buckets);\n\t\t\t}\n\t\t}\n\t}\n};\n\n/**\n * Fetches shared validation data for each field type with a validator.\n */\nconst buildValidationData = async (\n\tcontext: ServiceContext,\n\tbuckets: ValidationBuckets,\n): Promise<ValidationData> => {\n\tconst validationData: ValidationData = {};\n\tconst promises: Promise<void>[] = [];\n\n\tfor (const fieldType of registeredFieldTypes) {\n\t\tconst fieldDefinition = registeredFields[fieldType];\n\t\tif (fieldDefinition.validateInput === null) continue;\n\n\t\tconst input = Object.entries(\n\t\t\tbuckets[fieldType] ?? {},\n\t\t).reduce<FieldRelationValidationInput>((acc, [groupKey, ids]) => {\n\t\t\tacc[groupKey] = Array.from(ids);\n\t\t\treturn acc;\n\t\t}, {});\n\n\t\tpromises.push(\n\t\t\tfieldDefinition.validateInput(context, input).then((result) => {\n\t\t\t\tvalidationData[fieldType] = result;\n\t\t\t}),\n\t\t);\n\t}\n\n\tawait Promise.all(promises);\n\treturn validationData;\n};\n\nexport default fetchValidationData;\n"],"mappings":"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"}