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.8 kB
{"version":3,"file":"utils.mjs","names":["constants"],"sources":["../../../src/libs/toolkit/utils.ts"],"sourcesContent":["import constants from \"../../constants/constants.js\";\nimport type {\n\tClientGetMultipleQueryParams,\n\tClientGetSingleQueryParams,\n} from \"../../schemas/documents.js\";\nimport type {\n\tQueryFilters,\n\tQueryParamFilters,\n} from \"../../types/query-params.js\";\nimport decodeError from \"../../utils/errors/decode-error.js\";\nimport flattenDocumentFilters from \"../../utils/helpers/flatten-document-filters.js\";\nimport type {\n\tServiceContext,\n\tServiceResponse,\n} from \"../../utils/services/types.js\";\nimport { copy } from \"../i18n/index.js\";\nimport type { ToolkitTenantOptions } from \"./types.js\";\n\ntype PaginatedQuery = {\n\tpage?: number;\n\tperPage?: number;\n};\n\ntype DocumentQuery = {\n\tfilter?: QueryFilters | QueryFilters[];\n};\n\ntype ResolvedServiceResponse<T> = Awaited<ServiceResponse<T>>;\n\ntype ToolkitServiceErrorCopy = {\n\tkey: string;\n\tdefaultMessage: string;\n};\n\ntype ToolkitServiceErrorConfig = {\n\tname?: ToolkitServiceErrorCopy;\n\tmessage: ToolkitServiceErrorCopy;\n};\n\ntype ServiceDocumentFilter = NonNullable<\n\tClientGetSingleQueryParams[\"filter\"] | ClientGetMultipleQueryParams[\"filter\"]\n>;\ntype ServiceDocumentFilterOr = NonNullable<\n\t| ClientGetSingleQueryParams[\"filterOr\"]\n\t| ClientGetMultipleQueryParams[\"filterOr\"]\n>;\n\ntype NormalizedDocumentFilters = {\n\tfilter?: ServiceDocumentFilter;\n\tfilterOr?: ServiceDocumentFilterOr;\n};\n\n/** Converts one flattened filter map into an AND group for filterOr. */\nconst toFilterGroup = (\n\tfilters?: QueryParamFilters,\n): ServiceDocumentFilterOr[number] =>\n\tObject.entries(filters ?? {}).map(([key, filter]) => ({\n\t\tkey,\n\t\t...filter,\n\t})) as ServiceDocumentFilterOr[number];\n\n/** Maps toolkit filter shorthand to the service filter/filterOr shape. */\nconst normalizeToolkitDocumentFilters = (\n\tfilters?: QueryFilters | QueryFilters[],\n): NormalizedDocumentFilters => {\n\tif (!filters) return {};\n\n\tif (!Array.isArray(filters)) {\n\t\treturn {\n\t\t\tfilter: flattenDocumentFilters(filters) as ServiceDocumentFilter,\n\t\t};\n\t}\n\n\tconst filterOr = filters\n\t\t.map((filterGroup) => toFilterGroup(flattenDocumentFilters(filterGroup)))\n\t\t.filter((filterGroup) => filterGroup.length > 0);\n\n\treturn filterOr.length > 0 ? { filterOr } : {};\n};\n\n/** Applies Lucid's default pagination when toolkit callers omit it. */\nexport const normalizePaginatedQuery = <T extends PaginatedQuery>(\n\tquery?: T,\n): Omit<T, \"page\" | \"perPage\"> & { page: number; perPage: number } => {\n\tconst normalizedQuery = query ?? ({} as T);\n\n\treturn {\n\t\t...normalizedQuery,\n\t\tpage: normalizedQuery.page ?? constants.query.page,\n\t\tperPage: normalizedQuery.perPage ?? constants.query.perPage,\n\t};\n};\n\n/** Flattens nested document filters so toolkit calls match the internal service query shape. */\nexport const normalizeDocumentQuery = <T extends DocumentQuery>(\n\tquery?: T,\n): Omit<T, \"filter\"> & NormalizedDocumentFilters => {\n\tconst normalizedQuery = query ?? ({} as T);\n\tconst { filter, ...restQuery } = normalizedQuery;\n\n\treturn {\n\t\t...restQuery,\n\t\t...normalizeToolkitDocumentFilters(filter),\n\t};\n};\n\n/** Applies pagination defaults and flattens nested document filters for toolkit document queries. */\nexport const normalizePaginatedDocumentQuery = <\n\tT extends PaginatedQuery & DocumentQuery,\n>(\n\tquery?: T,\n): Omit<T, \"filter\" | \"page\" | \"perPage\"> & {\n\tfilter?: ServiceDocumentFilter;\n\tfilterOr?: ServiceDocumentFilterOr;\n\tpage: number;\n\tperPage: number;\n} => {\n\tconst normalizedQuery = query ?? ({} as T);\n\tconst { filter, page, perPage, ...restQuery } = normalizedQuery;\n\n\treturn {\n\t\t...restQuery,\n\t\t...normalizeToolkitDocumentFilters(filter),\n\t\tpage: page ?? constants.query.page,\n\t\tperPage: perPage ?? constants.query.perPage,\n\t};\n};\n\n/** Clones optional query objects so toolkit services can pass a stable shape downstream. */\nexport const normalizeQuery = <T extends object>(query?: T): T =>\n\t({ ...(query ?? {}) }) as T;\n\n/** Creates a per-call toolkit context when a tenant override is supplied. */\nexport const withToolkitTenant = (\n\tcontext: ServiceContext,\n\toptions?: ToolkitTenantOptions,\n): ServiceContext => {\n\tif (options?.tenantKey === undefined) return context;\n\n\treturn {\n\t\t...context,\n\t\trequest: {\n\t\t\t...context.request,\n\t\t\ttenantKey: options.tenantKey,\n\t\t},\n\t};\n};\n\n/** Converts unexpected exceptions into standard Lucid service error values. */\nexport const runToolkitService = async <T>(\n\tcallback: () => ServiceResponse<T>,\n\terrorConfig: ToolkitServiceErrorConfig,\n): ServiceResponse<T> => {\n\ttry {\n\t\tconst response = await callback();\n\n\t\tif (response.error || response.data !== undefined) {\n\t\t\treturn response;\n\t\t}\n\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tname: errorConfig.name\n\t\t\t\t\t? copy(`server:${errorConfig.name.key}`, {\n\t\t\t\t\t\t\tdefaultMessage: errorConfig.name.defaultMessage,\n\t\t\t\t\t\t})\n\t\t\t\t\t: undefined,\n\t\t\t\tmessage: copy(`server:${errorConfig.message.key}`, {\n\t\t\t\t\tdefaultMessage: errorConfig.message.defaultMessage,\n\t\t\t\t}),\n\t\t\t\tstatus: 500,\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t} satisfies ResolvedServiceResponse<T>;\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconst decodedError = decodeError(error);\n\n\t\t\treturn {\n\t\t\t\terror: {\n\t\t\t\t\ttype: \"basic\",\n\t\t\t\t\tname: errorConfig.name\n\t\t\t\t\t\t? copy(`server:${errorConfig.name.key}`, {\n\t\t\t\t\t\t\t\tdefaultMessage: errorConfig.name.defaultMessage,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: copy(\"server:core.errors.default.name\", {\n\t\t\t\t\t\t\t\tdefaultMessage: decodedError.name,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\tmessage: copy(`server:${errorConfig.message.key}`, {\n\t\t\t\t\t\tdefaultMessage: errorConfig.message.defaultMessage,\n\t\t\t\t\t}),\n\t\t\t\t\tstatus: decodedError.status,\n\t\t\t\t\tcode: decodedError.code,\n\t\t\t\t\terrors: undefined,\n\t\t\t\t},\n\t\t\t\tdata: undefined,\n\t\t\t} satisfies ResolvedServiceResponse<T>;\n\t\t}\n\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tname: errorConfig.name\n\t\t\t\t\t? copy(`server:${errorConfig.name.key}`, {\n\t\t\t\t\t\t\tdefaultMessage: errorConfig.name.defaultMessage,\n\t\t\t\t\t\t})\n\t\t\t\t\t: copy(\"server:core.errors.default.name\", {\n\t\t\t\t\t\t\tdefaultMessage: constants.errors.name,\n\t\t\t\t\t\t}),\n\t\t\t\tmessage: copy(`server:${errorConfig.message.key}`, {\n\t\t\t\t\tdefaultMessage: errorConfig.message.defaultMessage,\n\t\t\t\t}),\n\t\t\t\tstatus: constants.errors.status,\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t} satisfies ResolvedServiceResponse<T>;\n\t}\n};\n"],"mappings":"wMAqDA,MAAM,EACL,GAEA,OAAO,QAAQ,GAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAK,MAAa,CACrD,MACA,GAAG,CACJ,EAAE,EAGG,EACL,GAC+B,CAC/B,GAAI,CAAC,EAAS,MAAO,CAAC,EAEtB,GAAI,CAAC,MAAM,QAAQ,CAAO,EACzB,MAAO,CACN,OAAQ,EAAuB,CAAO,CACvC,EAGD,IAAM,EAAW,EACf,IAAK,GAAgB,EAAc,EAAuB,CAAW,CAAC,CAAC,CAAC,CACxE,OAAQ,GAAgB,EAAY,OAAS,CAAC,EAEhD,OAAO,EAAS,OAAS,EAAI,CAAE,UAAS,EAAI,CAAC,CAC9C,EAGa,EACZ,GACqE,CACrE,IAAM,EAAkB,GAAU,CAAC,EAEnC,MAAO,CACN,GAAG,EACH,KAAM,EAAgB,MAAQA,EAAU,MAAM,KAC9C,QAAS,EAAgB,SAAWA,EAAU,MAAM,OACrD,CACD,EAGa,EACZ,GACmD,CAEnD,GAAM,CAAE,SAAQ,GAAG,GADK,GAAU,CAAC,EAGnC,MAAO,CACN,GAAG,EACH,GAAG,EAAgC,CAAM,CAC1C,CACD,EAGa,EAGZ,GAMI,CAEJ,GAAM,CAAE,SAAQ,OAAM,UAAS,GAAG,GADV,GAAU,CAAC,EAGnC,MAAO,CACN,GAAG,EACH,GAAG,EAAgC,CAAM,EACzC,KAAM,GAAQA,EAAU,MAAM,KAC9B,QAAS,GAAWA,EAAU,MAAM,OACrC,CACD,EAGa,EAAoC,IAC/C,CAAE,GAAI,GAAS,CAAC,CAAG,GAGR,GACZ,EACA,IAEI,GAAS,YAAc,IAAA,GAAkB,EAEtC,CACN,GAAG,EACH,QAAS,CACR,GAAG,EAAQ,QACX,UAAW,EAAQ,SACpB,CACD,EAIY,EAAoB,MAChC,EACA,IACwB,CACxB,GAAI,CACH,IAAM,EAAW,MAAM,EAAS,EAMhC,OAJI,EAAS,OAAS,EAAS,OAAS,IAAA,GAChC,EAGD,CACN,MAAO,CACN,KAAM,QACN,KAAM,EAAY,KACf,EAAK,UAAU,EAAY,KAAK,MAAO,CACvC,eAAgB,EAAY,KAAK,cAClC,CAAC,EACA,IAAA,GACH,QAAS,EAAK,UAAU,EAAY,QAAQ,MAAO,CAClD,eAAgB,EAAY,QAAQ,cACrC,CAAC,EACD,OAAQ,GACT,EACA,KAAM,IAAA,EACP,CACD,OAAS,EAAO,CACf,GAAI,aAAiB,MAAO,CAC3B,IAAM,EAAe,EAAY,CAAK,EAEtC,MAAO,CACN,MAAO,CACN,KAAM,QACN,KAAM,EAAY,KACf,EAAK,UAAU,EAAY,KAAK,MAAO,CACvC,eAAgB,EAAY,KAAK,cAClC,CAAC,EACA,EAAK,kCAAmC,CACxC,eAAgB,EAAa,IAC9B,CAAC,EACH,QAAS,EAAK,UAAU,EAAY,QAAQ,MAAO,CAClD,eAAgB,EAAY,QAAQ,cACrC,CAAC,EACD,OAAQ,EAAa,OACrB,KAAM,EAAa,KACnB,OAAQ,IAAA,EACT,EACA,KAAM,IAAA,EACP,CACD,CAEA,MAAO,CACN,MAAO,CACN,KAAM,QACN,KAAM,EAAY,KACf,EAAK,UAAU,EAAY,KAAK,MAAO,CACvC,eAAgB,EAAY,KAAK,cAClC,CAAC,EACA,EAAK,kCAAmC,CACxC,eAAgBA,EAAU,OAAO,IAClC,CAAC,EACH,QAAS,EAAK,UAAU,EAAY,QAAQ,MAAO,CAClD,eAAgB,EAAY,QAAQ,cACrC,CAAC,EACD,OAAQA,EAAU,OAAO,MAC1B,EACA,KAAM,IAAA,EACP,CACD,CACD"}