@bitrix24/b24jssdk
Version:
Bitrix24 REST API JavaScript SDK
1 lines • 6.6 kB
Source Map (JSON)
{"version":3,"file":"parse-error-payload.mjs","sources":["../../../../src/core/http/parse-error-payload.ts"],"sourcesContent":["import type { TypeDescriptionError, TypeDescriptionErrorV3 } from '../../types/auth'\n\n/**\n * One entry of the `restApi:v3` `validation` array, as the SDK exposes it.\n *\n * `field` and `message` are both optional because the portal's own type says so\n * — `TypeDescriptionErrorV3` declares them optional and permits extra keys. A\n * caller matching on `field` has to handle it being absent; that is the portal's\n * shape, not a hedge.\n */\nexport type ValidationDetail = {\n readonly field?: string\n readonly message?: string\n readonly [key: string]: unknown\n}\n\n/** What a REST error body yields, whichever protocol version produced it. */\nexport type ParsedErrorPayload = {\n readonly code: string\n readonly description: string\n /**\n * The `restApi:v3` `validation` array, verbatim and **unredacted** — this is\n * the raw read. `AjaxError` runs each entry through `redactSensitiveParams`\n * when it stores them, which is the copy a caller sees.\n *\n * Absent for `restApi:v2`, which has no equivalent, and absent when v3 did not\n * send one.\n */\n readonly validation?: readonly ValidationDetail[]\n}\n\n/**\n * Separates the messages folded into {@link ParsedErrorPayload.description}.\n *\n * Two validation rows used to be concatenated with nothing between them —\n * `Field A must not be empty.Field B is invalid.` — with no way to tell where\n * one ended (#423).\n */\nconst MESSAGE_SEPARATOR = ' '\n\n/**\n * Reads a Bitrix24 REST error body into the parts the SDK reports.\n *\n * This existed twice — in `AjaxResult.#processErrors()` and in\n * `AbstractHttp._convertAxiosErrorToAjaxError()` — each carrying a\n * `@todo make single function` pointing at the other. Which copy ran depended on\n * whether the error code was soft or hard, something a caller does not control,\n * so the two had to agree and nothing made them. They did not: neither read\n * `validation[].field`, and a fix applied to one would have left the other\n * lossy (#423).\n *\n * Returns `undefined` when the body carries no error the SDK recognises, so a\n * caller can tell \"no error here\" from \"an error with an empty description\".\n *\n * @param body the parsed response body — `unknown` because it arrives from\n * `axios` on one path and from a stored response on the other, and neither is\n * trustworthy enough to assert a type on.\n * @param fallbackCode used when the body names no code, e.g. an axios error\n * whose own code is the only one available.\n * @param fallbackDescription likewise for the message.\n */\nexport function parseErrorPayload(\n body: unknown,\n fallbackCode: string,\n fallbackDescription: string\n): ParsedErrorPayload | undefined {\n if (!body || typeof body !== 'object' || !('error' in body)) {\n return undefined\n }\n\n const responseData = body as TypeDescriptionError | TypeDescriptionErrorV3\n\n // restApi:v3 — `error` is an object carrying `code`, and optionally the\n // `validation` array this function exists to stop losing.\n if (\n responseData.error\n && typeof responseData.error === 'object'\n && 'code' in responseData.error\n ) {\n const error = responseData.error\n let description = String(error.message ?? '').trimEnd()\n\n if (error.validation && error.validation.length > 0) {\n const messages = error.validation\n // `||`, not `??`: a row whose `message` is present but empty says\n // nothing, so it falls back to the row itself rather than contributing\n // an empty fragment and a stray separator.\n .map(row => row?.message || JSON.stringify(row))\n .filter(message => message !== undefined && message !== '')\n\n if (messages.length > 0) {\n if (description.length > 0) {\n if (!description.endsWith('.')) {\n description += '.'\n }\n description += MESSAGE_SEPARATOR\n }\n description += messages.join(MESSAGE_SEPARATOR)\n }\n }\n\n return {\n code: error.code,\n description,\n // Kept verbatim rather than reshaped: `field` is what the caller came for,\n // and the portal is free to add keys the SDK has not seen. The copy is\n // what detaches it from the response body; the freeze stops a caller\n // reordering or extending the array. Both are **shallow** — the `readonly`\n // on each entry's fields is a type-level claim only, and nothing stops a\n // caller writing to `validation[0].field` at runtime.\n ...(error.validation ? { validation: Object.freeze([...error.validation]) } : {})\n }\n }\n\n // restApi:v2 — `error` is the code itself, with the text alongside it.\n if (responseData.error && typeof responseData.error === 'string') {\n return {\n code: responseData.error !== '0' ? responseData.error : fallbackCode,\n description: (responseData as TypeDescriptionError)?.error_description ?? fallbackDescription\n }\n }\n\n return undefined\n}\n"],"names":[],"mappings":";;;;;;;;;;AAsCA,MAAM,iBAAA,GAAoB,GAAA;AAuBnB,SAAS,iBAAA,CACd,IAAA,EACA,YAAA,EACA,mBAAA,EACgC;AAChC,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,SAAS,QAAA,IAAY,EAAE,WAAW,IAAA,CAAA,EAAO;AAC3D,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,IAAA;AAIrB,EAAA,IACE,YAAA,CAAa,SACV,OAAO,YAAA,CAAa,UAAU,QAAA,IAC9B,MAAA,IAAU,aAAa,KAAA,EAC1B;AACA,IAAA,MAAM,QAAQ,YAAA,CAAa,KAAA;AAC3B,IAAA,IAAI,cAAc,MAAA,CAAO,KAAA,CAAM,OAAA,IAAW,EAAE,EAAE,OAAA,EAAQ;AAEtD,IAAA,IAAI,KAAA,CAAM,UAAA,IAAc,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA,EAAG;AACnD,MAAA,MAAM,WAAW,KAAA,CAAM,UAAA,CAIpB,GAAA,CAAI,CAAA,GAAA,KAAO,KAAK,OAAA,IAAW,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA,CAC9C,MAAA,CAAO,aAAW,OAAA,KAAY,MAAA,IAAa,YAAY,EAAE,CAAA;AAE5D,MAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,QAAA,IAAI,WAAA,CAAY,SAAS,CAAA,EAAG;AAC1B,UAAA,IAAI,CAAC,WAAA,CAAY,QAAA,CAAS,GAAG,CAAA,EAAG;AAC9B,YAAA,WAAA,IAAe,GAAA;AAAA,UACjB;AACA,UAAA,WAAA,IAAe,iBAAA;AAAA,QACjB;AACA,QAAA,WAAA,IAAe,QAAA,CAAS,KAAK,iBAAiB,CAAA;AAAA,MAChD;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,WAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,GAAI,KAAA,CAAM,UAAA,GAAa,EAAE,YAAY,MAAA,CAAO,MAAA,CAAO,CAAC,GAAG,KAAA,CAAM,UAAU,CAAC,CAAA,KAAM;AAAC,KACjF;AAAA,EACF;AAGA,EAAA,IAAI,YAAA,CAAa,KAAA,IAAS,OAAO,YAAA,CAAa,UAAU,QAAA,EAAU;AAChE,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,YAAA,CAAa,KAAA,KAAU,GAAA,GAAM,aAAa,KAAA,GAAQ,YAAA;AAAA,MACxD,WAAA,EAAc,cAAuC,iBAAA,IAAqB;AAAA,KAC5E;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AA9DgB,MAAA,CAAA,iBAAA,EAAA,mBAAA,CAAA;;;;"}