@lucidcms/core
Version:
The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.
1 lines • 16.3 kB
Source Map (JSON)
{"version":3,"file":"client.mjs","names":["constants","packageJson.version"],"sources":["../../../src/libs/lucid-remote/client.ts"],"sourcesContent":["import packageJson from \"../../../package.json\" with { type: \"json\" };\nimport constants from \"../../constants/constants.js\";\nimport type { LucidErrorData } from \"../../types/errors.js\";\nimport type { ErrorResponse } from \"../../types/response.js\";\nimport { getBaseUrl } from \"../../utils/helpers/index.js\";\nimport type {\n\tServiceContext,\n\tServiceResponse,\n} from \"../../utils/services/types.js\";\nimport { copy } from \"../i18n/index.js\";\nimport type { LucidRemoteRequestData } from \"./types.js\";\n\ntype LucidRemoteRequestProps = {\n\tmethod?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\theaders?: HeadersInit;\n\tbody?: unknown;\n\torigin?: string;\n\tretries?: number;\n};\n\ntype LucidRemoteClient = {\n\trequest: <T>(\n\t\tpath: string,\n\t\tprops?: LucidRemoteRequestProps,\n\t) => ServiceResponse<LucidRemoteRequestData<T>>;\n};\n\ntype LucidRemoteErrorResponse = {\n\tstatus: number;\n\tcode?: string;\n\tkey?: string;\n\tname?: string;\n\tmessage: string;\n\terrors?: ErrorResponse[\"errors\"];\n};\n\nconst clients = new Map<string, LucidRemoteClient>();\n\n/**\n * Allows internal tests to point Lucid remote requests at a local service.\n * Production requests use the default remote domain.\n */\nconst getLucidRemoteApiDomain = (context: ServiceContext) => {\n\tconst override = context.env?.LUCID_CMS_INTERNAL_REMOTE_API_URL_OVERRIDE;\n\n\treturn (\n\t\ttypeof override === \"string\" && override.trim()\n\t\t\t? override.trim()\n\t\t\t: constants.endpoints.lucidRemoteApiDomain\n\t).replace(/\\/+$/, \"\");\n};\n\nconst isErrorResponse = (value: unknown): value is LucidRemoteErrorResponse => {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\n\tconst error = value as Partial<LucidRemoteErrorResponse>;\n\n\treturn (\n\t\ttypeof error.status === \"number\" &&\n\t\ttypeof error.message === \"string\" &&\n\t\t(error.code === undefined || typeof error.code === \"string\") &&\n\t\t(error.key === undefined || typeof error.key === \"string\") &&\n\t\t(error.name === undefined || typeof error.name === \"string\")\n\t);\n};\n\nconst getErrorCopy = (errorKey?: string) => {\n\tswitch (errorKey) {\n\t\tcase \"ai_invalid_license\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.license.invalid.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.license.invalid.message\"),\n\t\t\t};\n\t\tcase \"ai_license_key_required\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.license.key.required.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.license.key.required.message\"),\n\t\t\t};\n\t\tcase \"failed_to_load_cms_license\":\n\t\tcase \"failed_to_load_cms_licenses\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.license.verify.failed.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.license.verify.failed.message\"),\n\t\t\t};\n\t\tcase \"license_not_found\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.license.not.found.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.license.not.found.message\"),\n\t\t\t};\n\t\tcase \"organisation_ai_credit_exhausted\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.credit.exhausted.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.credit.exhausted.message\"),\n\t\t\t};\n\t\tcase \"organisation_ai_deferred_debit_outstanding\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.deferred.debit.outstanding.name\"),\n\t\t\t\tmessage: copy(\n\t\t\t\t\t\"server:core.ai.remote.deferred.debit.outstanding.message\",\n\t\t\t\t),\n\t\t\t};\n\t\tcase \"organisation_ai_top_up_requires_payment_method\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.top.up.payment.method.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.top.up.payment.method.message\"),\n\t\t\t};\n\t\tcase \"organisation_ai_top_up_pending\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.top.up.pending.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.top.up.pending.message\"),\n\t\t\t};\n\t\tcase \"organisation_ai_top_up_failed\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.top.up.failed.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.top.up.failed.message\"),\n\t\t\t};\n\t\tcase \"license_ai_period_cap_reached\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.period.cap.reached.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.period.cap.reached.message\"),\n\t\t\t};\n\t\tcase \"license_ai_disabled_for_usage\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.license.ai.disabled.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.license.ai.disabled.message\"),\n\t\t\t};\n\t\tcase \"license_deactivated_for_usage\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.license.deactivated.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.license.deactivated.message\"),\n\t\t\t};\n\t\tcase \"license_not_found_for_usage\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.license.not.found.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.license.not.found.message\"),\n\t\t\t};\n\t\tcase \"cms_ai_feature_not_supported\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.feature.not.supported.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.feature.not.supported.message\"),\n\t\t\t};\n\t\tcase \"ai_provider_configuration_missing\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.provider.configuration.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.provider.configuration.message\"),\n\t\t\t};\n\t\tcase \"ai_usage_pricing_missing\":\n\t\tcase \"ai_pricing_must_be_usd\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.pricing.configuration.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.pricing.configuration.message\"),\n\t\t\t};\n\t\tcase \"cms_ai_image_source_invalid\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.image.source.invalid.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.image.source.invalid.message\"),\n\t\t\t};\n\t\tcase \"cms_ai_image_storage_configuration_missing\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.image.storage.unavailable.name\"),\n\t\t\t\tmessage: copy(\n\t\t\t\t\t\"server:core.ai.remote.image.storage.unavailable.message\",\n\t\t\t\t),\n\t\t\t};\n\t\tcase \"cms_ai_generation_request_not_found\":\n\t\tcase \"cms_ai_generated_image_not_found\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.generation.not.found.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.generation.not.found.message\"),\n\t\t\t};\n\t\tcase \"cms_ai_idempotency_key_required\":\n\t\tcase \"cms_ai_idempotency_key_conflict\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.generation.request.invalid.name\"),\n\t\t\t\tmessage: copy(\n\t\t\t\t\t\"server:core.ai.remote.generation.request.invalid.message\",\n\t\t\t\t),\n\t\t\t};\n\t\tcase \"ai_provider_request_failed\":\n\t\tcase \"ai_provider_invalid_response\":\n\t\tcase \"ai_provider_output_truncated\":\n\t\t\treturn {\n\t\t\t\tname: copy(\"server:core.ai.remote.provider.failed.name\"),\n\t\t\t\tmessage: copy(\"server:core.ai.remote.provider.failed.message\"),\n\t\t\t};\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n};\n\nconst createRemoteError = (\n\tresponse: Response,\n\tjson: unknown,\n): { error: LucidErrorData; data: undefined } => {\n\tif (isErrorResponse(json)) {\n\t\tconst errorKey = json.key ?? json.code;\n\t\tconst errorCopy = getErrorCopy(errorKey);\n\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tstatus: json.status || response.status,\n\t\t\t\tkey: errorKey,\n\t\t\t\tname:\n\t\t\t\t\terrorCopy?.name ??\n\t\t\t\t\tcopy(\"server:core.lucid.remote.request.failed.name\", {\n\t\t\t\t\t\tdefaultMessage: json.name ?? \"Lucid remote request failed\",\n\t\t\t\t\t}),\n\t\t\t\tmessage:\n\t\t\t\t\terrorCopy?.message ??\n\t\t\t\t\tcopy(\"server:core.lucid.remote.request.failed\", {\n\t\t\t\t\t\tdefaultMessage: json.message,\n\t\t\t\t\t}),\n\t\t\t\terrors: json.errors as LucidErrorData[\"errors\"],\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t};\n\t}\n\n\treturn {\n\t\terror: {\n\t\t\ttype: \"basic\",\n\t\t\tstatus: response.status,\n\t\t\tmessage: copy(\"server:core.lucid.remote.request.failed\", {\n\t\t\t\tdefaultMessage: response.statusText || \"Lucid remote request failed.\",\n\t\t\t}),\n\t\t},\n\t\tdata: undefined,\n\t};\n};\n\nconst createLucidRemoteClient = (props: {\n\tapiDomain: string;\n\torigin: string;\n}): LucidRemoteClient => ({\n\trequest: async <T>(\n\t\tpath: string,\n\t\trequestProps: LucidRemoteRequestProps = {},\n\t) => {\n\t\tconst retries = requestProps.retries ?? 0;\n\t\tconst url = new URL(path, props.apiDomain).toString();\n\t\tconst headers = new Headers(requestProps.headers);\n\n\t\tif (!headers.has(\"Accept\")) {\n\t\t\theaders.set(\"Accept\", \"application/json\");\n\t\t}\n\n\t\tif (!headers.has(\"User-Agent\")) {\n\t\t\theaders.set(\"User-Agent\", `LucidCMS/${packageJson.version}`);\n\t\t}\n\n\t\tif (!headers.has(\"Origin\")) {\n\t\t\theaders.set(\"Origin\", requestProps.origin ?? props.origin);\n\t\t}\n\n\t\tconst body =\n\t\t\trequestProps.body === undefined\n\t\t\t\t? undefined\n\t\t\t\t: typeof requestProps.body === \"string\"\n\t\t\t\t\t? requestProps.body\n\t\t\t\t\t: JSON.stringify(requestProps.body);\n\n\t\tif (body !== undefined && !headers.has(\"Content-Type\")) {\n\t\t\theaders.set(\"Content-Type\", \"application/json\");\n\t\t}\n\n\t\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(url, {\n\t\t\t\t\tmethod: requestProps.method ?? \"GET\",\n\t\t\t\t\theaders,\n\t\t\t\t\tbody,\n\t\t\t\t});\n\n\t\t\t\tif (response.status >= 500 && attempt < retries) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst responseBody = await response.text();\n\t\t\t\tlet json: unknown;\n\n\t\t\t\ttry {\n\t\t\t\t\tjson = responseBody ? JSON.parse(responseBody) : {};\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\ttype: \"basic\",\n\t\t\t\t\t\t\tstatus: response.status || 500,\n\t\t\t\t\t\t\tmessage: copy(\"server:core.lucid.remote.request.failed\", {\n\t\t\t\t\t\t\t\tdefaultMessage:\n\t\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t\t\t\t\t: \"Lucid remote request failed.\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdata: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\treturn createRemoteError(response, json);\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\terror: undefined,\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\tjson: json as LucidRemoteRequestData<T>[\"json\"],\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tif (attempt < retries) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\terror: {\n\t\t\t\t\t\ttype: \"basic\",\n\t\t\t\t\t\tstatus: 500,\n\t\t\t\t\t\tmessage: copy(\"server:core.lucid.remote.request.failed\", {\n\t\t\t\t\t\t\tdefaultMessage:\n\t\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t\t\t\t: \"Lucid remote request failed.\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tdata: undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tstatus: 500,\n\t\t\t\tmessage: copy(\"server:core.lucid.remote.request.failed\", {\n\t\t\t\t\tdefaultMessage: \"Lucid remote request failed.\",\n\t\t\t\t}),\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t};\n\t},\n});\n\n/**\n * Returns a cached client for Lucid-owned remote services.\n * Centralises remote domain config, CMS origin headers, JSON parsing, retries,\n * and conversion of remote Lucid errors into core service errors.\n */\nexport const getLucidRemoteClient = (context: ServiceContext) => {\n\tconst apiDomain = getLucidRemoteApiDomain(context);\n\tconst origin = getBaseUrl(context);\n\tconst cacheKey = `${apiDomain}:${origin}`;\n\tconst cachedClient = clients.get(cacheKey);\n\n\tif (cachedClient) {\n\t\treturn cachedClient;\n\t}\n\n\tconst client = createLucidRemoteClient({\n\t\tapiDomain,\n\t\torigin,\n\t});\n\tclients.set(cacheKey, client);\n\n\treturn client;\n};\n"],"mappings":"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"}