123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import type {JsonToken} from './constants';
- import {
- ARR,
- ARR_VAL,
- ARR_VAL_COMPLETED,
- ARR_VAL_STR,
- OBJ,
- OBJ_KEY,
- OBJ_KEY_STR,
- OBJ_VAL,
- OBJ_VAL_COMPLETED,
- OBJ_VAL_STR,
- } from './constants';
- const ALLOWED_PRIMITIVES = ['true', 'false', 'null'];
- /**
- * Complete an incomplete JSON string.
- * This will ensure that the last element always has a `"~~"` to indicate it was truncated.
- * For example, `[1,2,` will be completed to `[1,2,"~~"]`
- * and `{"aa":"b` will be completed to `{"aa":"b~~"}`
- */
- export function completeJson(incompleteJson: string, stack: JsonToken[]): string {
- if (!stack.length) {
- return incompleteJson;
- }
- let json = incompleteJson;
- // Most checks are only needed for the last step in the stack
- const lastPos = stack.length - 1;
- const lastStep = stack[lastPos];
- json = _fixLastStep(json, lastStep);
- // Complete remaining steps - just add closing brackets
- for (let i = lastPos; i >= 0; i--) {
- const step = stack[i];
- // eslint-disable-next-line default-case
- switch (step) {
- case OBJ:
- json = `${json}}`;
- break;
- case ARR:
- json = `${json}]`;
- break;
- }
- }
- return json;
- }
- function _fixLastStep(json: string, lastStep: JsonToken): string {
- switch (lastStep) {
- // Object cases
- case OBJ:
- return `${json}"~~":"~~"`;
- case OBJ_KEY:
- return `${json}:"~~"`;
- case OBJ_KEY_STR:
- return `${json}~~":"~~"`;
- case OBJ_VAL:
- return _maybeFixIncompleteObjValue(json);
- case OBJ_VAL_STR:
- return `${json}~~"`;
- case OBJ_VAL_COMPLETED:
- return `${json},"~~":"~~"`;
- // Array cases
- case ARR:
- return `${json}"~~"`;
- case ARR_VAL:
- return _maybeFixIncompleteArrValue(json);
- case ARR_VAL_STR:
- return `${json}~~"`;
- case ARR_VAL_COMPLETED:
- return `${json},"~~"`;
- default:
- return json;
- }
- }
- function _maybeFixIncompleteArrValue(json: string): string {
- const pos = _findLastArrayDelimiter(json);
- if (pos > -1) {
- const part = json.slice(pos + 1);
- if (ALLOWED_PRIMITIVES.includes(part.trim())) {
- return `${json},"~~"`;
- }
- // Everything else is replaced with `"~~"`
- return `${json.slice(0, pos + 1)}"~~"`;
- }
- // fallback, this shouldn't happen, to be save
- return json;
- }
- function _findLastArrayDelimiter(json: string): number {
- for (let i = json.length - 1; i >= 0; i--) {
- const char = json[i];
- if (char === ',' || char === '[') {
- return i;
- }
- }
- return -1;
- }
- function _maybeFixIncompleteObjValue(json: string): string {
- const startPos = json.lastIndexOf(':');
- const part = json.slice(startPos + 1);
- if (ALLOWED_PRIMITIVES.includes(part.trim())) {
- return `${json},"~~":"~~"`;
- }
- // Everything else is replaced with `"~~"`
- // This also means we do not have incomplete numbers, e.g `[1` is replaced with `["~~"]`
- return `${json.slice(0, startPos + 1)}"~~"`;
- }
|