@lucidcms/core
Version:
The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.
1 lines • 11.9 kB
Source Map (JSON)
{"version":3,"file":"document-bricks.mjs","names":["formatter","documentFieldsFormatter"],"sources":["../../../src/libs/formatters/document-bricks.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport type { FieldRefResponse } from \"../../services/documents-bricks/helpers/fetch-ref-data.js\";\nimport type { InternalDocumentBrick } from \"../../types/response.js\";\nimport type {\n\tConfig,\n\tInternalDocumentField,\n\tLucidBricksTable,\n\tLucidBrickTableName,\n\tSelect,\n} from \"../../types.js\";\nimport type CollectionBuilder from \"../collection/builders/collection-builder/index.js\";\nimport {\n\tgetFieldDatabaseConfig,\n\tisStorageMode,\n\tisTreeTableType,\n} from \"../collection/custom-fields/storage/index.js\";\nimport type { CollectionSchemaTable } from \"../collection/schema/types.js\";\nimport type { BrickQueryResponse } from \"../repositories/document-bricks.js\";\nimport type { DocumentQueryResponse } from \"../repositories/documents.js\";\nimport formatter, { documentFieldsFormatter } from \"./index.js\";\n\nconst formatMultiple = (props: {\n\tbricksQuery: BrickQueryResponse | DocumentQueryResponse;\n\tcollection: CollectionBuilder;\n\tbricksSchema: Array<CollectionSchemaTable<LucidBrickTableName>>;\n\trefData: FieldRefResponse;\n\tconfig: Config;\n\thost: string;\n}): InternalDocumentBrick[] => {\n\tconst brickSchemas = props.bricksSchema.filter(\n\t\t(schema) => schema.type === \"brick\",\n\t);\n\tif (brickSchemas.length === 0) return [];\n\n\tconst brickResponses: InternalDocumentBrick[] = [];\n\n\tfor (const schema of brickSchemas) {\n\t\tconst tableData = props.bricksQuery[schema.name];\n\t\tif (!tableData || tableData.length === 0) continue;\n\n\t\tconst rowsByBrickInstanceId = Map.groupBy(\n\t\t\ttableData,\n\t\t\t(item) => item.brick_instance_id,\n\t\t);\n\n\t\tfor (const [position, rows] of rowsByBrickInstanceId.entries()) {\n\t\t\tif (position === undefined || !rows || rows.length === 0) continue;\n\n\t\t\t//* take the first row to get the brick metadata, open value is shared acdross locale rows for now\n\t\t\tconst firstRow = rows[0];\n\t\t\tif (!firstRow) continue;\n\t\t\tif (!firstRow.brick_type) continue;\n\n\t\t\tconst brickKey = schema.key.brick;\n\t\t\tif (!brickKey) continue;\n\n\t\t\tconst brickBuilder = props.collection.brickInstances.find(\n\t\t\t\t(b) => b.key === brickKey,\n\t\t\t);\n\t\t\tif (!brickBuilder) continue;\n\n\t\t\tbrickResponses.push({\n\t\t\t\tref: generateBrickRef(props.collection.key, brickKey, firstRow.id),\n\t\t\t\tkey: brickKey,\n\t\t\t\torder: firstRow.position,\n\t\t\t\topen: formatter.formatBoolean(firstRow.is_open),\n\t\t\t\ttype: firstRow.brick_type,\n\t\t\t\tid: firstRow.id,\n\t\t\t\tfields: documentFieldsFormatter.formatMultiple(\n\t\t\t\t\t{\n\t\t\t\t\t\tbrickRows: rows,\n\t\t\t\t\t\tbricksQuery: props.bricksQuery,\n\t\t\t\t\t\tbricksSchema: props.bricksSchema,\n\t\t\t\t\t\trefData: props.refData,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\thost: props.host,\n\t\t\t\t\t\tbuilder: brickBuilder,\n\t\t\t\t\t\tcollection: props.collection,\n\t\t\t\t\t\tlocalization: {\n\t\t\t\t\t\t\tlocales: props.config.localization.locales.map((l) => l.code),\n\t\t\t\t\t\t\tdefault: props.config.localization.defaultLocale,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbrickKey: brickKey,\n\t\t\t\t\t\tconfig: props.config,\n\t\t\t\t\t\tbricksTableSchema: props.bricksSchema,\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn brickResponses.sort((a, b) => a.order - b.order);\n};\n\nconst formatDocumentFields = (props: {\n\tbricksQuery: BrickQueryResponse | DocumentQueryResponse;\n\tcollection: CollectionBuilder;\n\tbricksSchema: Array<CollectionSchemaTable<LucidBrickTableName>>;\n\trefData: FieldRefResponse;\n\tconfig: Config;\n\thost: string;\n}): InternalDocumentField[] => {\n\tconst documentFieldsSchema = props.bricksSchema.find(\n\t\t(bs) => bs.type === \"document-fields\",\n\t);\n\tif (!documentFieldsSchema) return [];\n\n\tconst tableData = props.bricksQuery[documentFieldsSchema.name];\n\tif (!tableData) return [];\n\n\tconst rowsByPos = Map.groupBy(tableData, (item) => item.position);\n\tconst rowOne = rowsByPos.get(0);\n\n\t//* there should always be no more than 1\n\tif (!rowOne) return [];\n\n\treturn documentFieldsFormatter.formatMultiple(\n\t\t{\n\t\t\tbrickRows: rowOne,\n\t\t\tbricksQuery: props.bricksQuery,\n\t\t\tbricksSchema: props.bricksSchema,\n\t\t\trefData: props.refData,\n\t\t},\n\t\t{\n\t\t\thost: props.host,\n\t\t\tbuilder: props.collection,\n\t\t\tcollection: props.collection,\n\t\t\tlocalization: {\n\t\t\t\tlocales: props.config.localization.locales.map((l) => l.code),\n\t\t\t\tdefault: props.config.localization.defaultLocale,\n\t\t\t},\n\t\t\tbrickKey: undefined,\n\t\t\tconfig: props.config,\n\t\t\tbricksTableSchema: props.bricksSchema,\n\t\t},\n\t);\n};\n\n/**\n * Resolves the target tree-table field rows based on schema path metadata.\n */\nconst getBrickTreeRows = (props: {\n\tbricksQuery: BrickQueryResponse | DocumentQueryResponse;\n\tbricksSchema: Array<CollectionSchemaTable<LucidBrickTableName>>;\n\tcollectionKey: string;\n\tbrickKey: string | undefined; // document-fields type doesnt add a brick key,\n\ttreeFieldKey: string;\n\ttreeLevel: number;\n\t/** Filters the response based on root IDs or parent IDs depending on tree depth. */\n\trelationIds: number[];\n}): Select<LucidBricksTable>[] => {\n\tconst matchingSchema = props.bricksSchema.find((schema) => {\n\t\t//* check if the collection key doesnt match\n\t\tif (schema.key.collection !== props.collectionKey) return false;\n\t\t//* match the brick key if provided\n\t\tif (props.brickKey !== undefined && schema.key.brick !== props.brickKey)\n\t\t\treturn false;\n\t\t//* check if this schema belongs to a tree-table storage mode\n\t\tif (!isTreeTableType(schema.type)) return false;\n\t\t//* for document fields without a brick key\n\t\tif (props.brickKey === undefined && schema.key.brick !== undefined)\n\t\t\treturn false;\n\t\t//* check if the tree field path exists\n\t\tconst treePath = schema.key.fieldPath;\n\t\tif (!treePath || treePath.length === 0) return false;\n\t\t//* ensure we're at the correct nesting level\n\t\tif (treePath.length !== props.treeLevel + 1) return false;\n\t\t//* check tree field key if it matches the last path segment\n\t\tif (treePath[treePath.length - 1] !== props.treeFieldKey) return false;\n\n\t\treturn true;\n\t});\n\n\tif (matchingSchema && matchingSchema.name in props.bricksQuery) {\n\t\tconst rows = props.bricksQuery[matchingSchema.name];\n\t\tif (!rows) return [];\n\n\t\tif (props.treeLevel === 0) {\n\t\t\t//* depth 0 tree tables are linked to the root brick/document-fields rows\n\t\t\treturn rows.filter(\n\t\t\t\t(r) => r.brick_id && props.relationIds.includes(r.brick_id),\n\t\t\t);\n\t\t}\n\n\t\t//* nested tree tables are linked by parent_id\n\t\treturn rows.filter(\n\t\t\t(r) => r.parent_id && props.relationIds.includes(r.parent_id),\n\t\t);\n\t}\n\treturn [];\n};\n\n/**\n * Resolves the target relation-table rows linked to the provided parent rows.\n */\nconst getRelationRows = (props: {\n\tbricksQuery: BrickQueryResponse | DocumentQueryResponse;\n\tbricksSchema: Array<CollectionSchemaTable<LucidBrickTableName>>;\n\tcollectionKey: string;\n\tbrickKey: string | undefined;\n\tfieldKey: string;\n\trelationIds: number[];\n\ttableType: CollectionSchemaTable<LucidBrickTableName>[\"type\"];\n}): Select<LucidBricksTable>[] => {\n\tconst databaseConfig = getFieldDatabaseConfig(props.tableType);\n\tif (!databaseConfig || !isStorageMode(databaseConfig, \"relation-table\")) {\n\t\treturn [];\n\t}\n\n\tconst matchingSchema = props.bricksSchema.find((schema) => {\n\t\tif (schema.key.collection !== props.collectionKey) return false;\n\t\tif (schema.type !== props.tableType) return false;\n\t\tif (props.brickKey !== undefined && schema.key.brick !== props.brickKey) {\n\t\t\treturn false;\n\t\t}\n\t\tif (props.brickKey === undefined && schema.key.brick !== undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst relationPath = schema.key.fieldPath;\n\t\tif (relationPath?.length !== 1) return false;\n\n\t\treturn relationPath[0] === props.fieldKey;\n\t});\n\n\tif (!matchingSchema || !(matchingSchema.name in props.bricksQuery)) {\n\t\treturn [];\n\t}\n\n\tconst rows = props.bricksQuery[matchingSchema.name];\n\tif (!rows) return [];\n\n\treturn rows.filter(\n\t\t(row) =>\n\t\t\ttypeof row.parent_id === \"number\" &&\n\t\t\tprops.relationIds.includes(row.parent_id),\n\t);\n};\n\n/**\n * Generates a unique deterministic reference for a brick\n */\nconst generateBrickRef = (\n\tcollectionKey: string,\n\tbrickKey: string,\n\tbrickInstanceId: number | string,\n): string => {\n\treturn crypto\n\t\t.createHash(\"sha256\")\n\t\t.update(`${collectionKey}-${brickKey}-${brickInstanceId}`)\n\t\t.digest(\"hex\")\n\t\t.substring(0, 36);\n};\n\nexport default {\n\tformatMultiple,\n\tformatDocumentFields,\n\tgetBrickTreeRows,\n\tgetRelationRows,\n};\n"],"mappings":"0NAqBA,MAAM,EAAkB,GAOO,CAC9B,IAAM,EAAe,EAAM,aAAa,OACtC,GAAW,EAAO,OAAS,OAC7B,EACA,GAAI,EAAa,SAAW,EAAG,MAAO,CAAC,EAEvC,IAAM,EAA0C,CAAC,EAEjD,IAAK,IAAM,KAAU,EAAc,CAClC,IAAM,EAAY,EAAM,YAAY,EAAO,MAC3C,GAAI,CAAC,GAAa,EAAU,SAAW,EAAG,SAE1C,IAAM,EAAwB,IAAI,QACjC,EACC,GAAS,EAAK,iBAChB,EAEA,IAAK,GAAM,CAAC,EAAU,KAAS,EAAsB,QAAQ,EAAG,CAC/D,GAAI,IAAa,IAAA,IAAa,CAAC,GAAQ,EAAK,SAAW,EAAG,SAG1D,IAAM,EAAW,EAAK,GAEtB,GADI,CAAC,GACD,CAAC,EAAS,WAAY,SAE1B,IAAM,EAAW,EAAO,IAAI,MAC5B,GAAI,CAAC,EAAU,SAEf,IAAM,EAAe,EAAM,WAAW,eAAe,KACnD,GAAM,EAAE,MAAQ,CAClB,EACK,GAEL,EAAe,KAAK,CACnB,IAAK,EAAiB,EAAM,WAAW,IAAK,EAAU,EAAS,EAAE,EACjE,IAAK,EACL,MAAO,EAAS,SAChB,KAAMA,EAAU,cAAc,EAAS,OAAO,EAC9C,KAAM,EAAS,WACf,GAAI,EAAS,GACb,OAAQC,EAAwB,eAC/B,CACC,UAAW,EACX,YAAa,EAAM,YACnB,aAAc,EAAM,aACpB,QAAS,EAAM,OAChB,EACA,CACC,KAAM,EAAM,KACZ,QAAS,EACT,WAAY,EAAM,WAClB,aAAc,CACb,QAAS,EAAM,OAAO,aAAa,QAAQ,IAAK,GAAM,EAAE,IAAI,EAC5D,QAAS,EAAM,OAAO,aAAa,aACpC,EACU,WACV,OAAQ,EAAM,OACd,kBAAmB,EAAM,YAC1B,CACD,CACD,CAAC,CACF,CACD,CAEA,OAAO,EAAe,MAAM,EAAG,IAAM,EAAE,MAAQ,EAAE,KAAK,CACvD,EAEM,EAAwB,GAOC,CAC9B,IAAM,EAAuB,EAAM,aAAa,KAC9C,GAAO,EAAG,OAAS,iBACrB,EACA,GAAI,CAAC,EAAsB,MAAO,CAAC,EAEnC,IAAM,EAAY,EAAM,YAAY,EAAqB,MACzD,GAAI,CAAC,EAAW,MAAO,CAAC,EAGxB,IAAM,EADY,IAAI,QAAQ,EAAY,GAAS,EAAK,QACjC,CAAC,CAAC,IAAI,CAAC,EAK9B,OAFK,EAEEA,EAAwB,eAC9B,CACC,UAAW,EACX,YAAa,EAAM,YACnB,aAAc,EAAM,aACpB,QAAS,EAAM,OAChB,EACA,CACC,KAAM,EAAM,KACZ,QAAS,EAAM,WACf,WAAY,EAAM,WAClB,aAAc,CACb,QAAS,EAAM,OAAO,aAAa,QAAQ,IAAK,GAAM,EAAE,IAAI,EAC5D,QAAS,EAAM,OAAO,aAAa,aACpC,EACA,SAAU,IAAA,GACV,OAAQ,EAAM,OACd,kBAAmB,EAAM,YAC1B,CACD,EArBoB,CAAC,CAsBtB,EAKM,EAAoB,GASQ,CACjC,IAAM,EAAiB,EAAM,aAAa,KAAM,GAAW,CAS1D,GAPI,EAAO,IAAI,aAAe,EAAM,eAEhC,EAAM,WAAa,IAAA,IAAa,EAAO,IAAI,QAAU,EAAM,UAG3D,CAAC,EAAgB,EAAO,IAAI,GAE5B,EAAM,WAAa,IAAA,IAAa,EAAO,IAAI,QAAU,IAAA,GACxD,MAAO,GAER,IAAM,EAAW,EAAO,IAAI,UAO5B,MAFA,EAJI,CAAC,GAAY,EAAS,SAAW,GAEjC,EAAS,SAAW,EAAM,UAAY,GAEtC,EAAS,EAAS,OAAS,KAAO,EAAM,aAG7C,CAAC,EAED,GAAI,GAAkB,EAAe,QAAQ,EAAM,YAAa,CAC/D,IAAM,EAAO,EAAM,YAAY,EAAe,MAW9C,OAVK,EAED,EAAM,YAAc,EAEhB,EAAK,OACV,GAAM,EAAE,UAAY,EAAM,YAAY,SAAS,EAAE,QAAQ,CAC3D,EAIM,EAAK,OACV,GAAM,EAAE,WAAa,EAAM,YAAY,SAAS,EAAE,SAAS,CAC7D,EAZkB,CAAC,CAapB,CACA,MAAO,CAAC,CACT,EAKM,EAAmB,GAQS,CACjC,IAAM,EAAiB,EAAuB,EAAM,SAAS,EAC7D,GAAI,CAAC,GAAkB,CAAC,EAAc,EAAgB,gBAAgB,EACrE,MAAO,CAAC,EAGT,IAAM,EAAiB,EAAM,aAAa,KAAM,GAAW,CAM1D,GALI,EAAO,IAAI,aAAe,EAAM,eAChC,EAAO,OAAS,EAAM,WACtB,EAAM,WAAa,IAAA,IAAa,EAAO,IAAI,QAAU,EAAM,UAG3D,EAAM,WAAa,IAAA,IAAa,EAAO,IAAI,QAAU,IAAA,GACxD,MAAO,GAGR,IAAM,EAAe,EAAO,IAAI,UAGhC,OAFI,GAAc,SAAW,GAEtB,EAAa,KAAO,EAAM,QAClC,CAAC,EAED,GAAI,CAAC,GAAkB,EAAE,EAAe,QAAQ,EAAM,aACrD,MAAO,CAAC,EAGT,IAAM,EAAO,EAAM,YAAY,EAAe,MAG9C,OAFK,EAEE,EAAK,OACV,GACA,OAAO,EAAI,WAAc,UACzB,EAAM,YAAY,SAAS,EAAI,SAAS,CAC1C,EANkB,CAAC,CAOpB,EAKM,GACL,EACA,EACA,IAEO,EACL,WAAW,QAAQ,CAAC,CACpB,OAAO,GAAG,EAAc,GAAG,EAAS,GAAG,GAAiB,CAAC,CACzD,OAAO,KAAK,CAAC,CACb,UAAU,EAAG,EAAE,EAGlB,IAAA,EAAe,CACd,iBACA,uBACA,mBACA,iBACD"}