1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16const typeErrorCode = 401; 17class BusinessError extends Error { 18 code: number; 19 constructor(msg: string) { 20 super(msg); 21 this.name = 'BusinessError'; 22 this.code = typeErrorCode; 23 } 24} 25 26type TransformsFunc = (this: Object, key: string, value: Object) => Object | undefined | null; 27type ReplacerType = (number | string)[] | null | TransformsFunc; 28 29const enum BigIntMode { 30 DEFAULT = 0, 31 PARSE_AS_BIGINT = 1, 32 ALWAYS_PARSE_AS_BIGINT = 2, 33} 34 35interface ParseOptions { 36 bigIntMode?: BigIntMode; 37} 38 39export interface JSON { 40 parseBigInt(text: string, reviver?: TransformsFunc, options?: ParseOptions): Object | null; 41 stringifyBigInt(value: Object, replacer?: TransformsFunc, space?: string | number): string; 42 stringifyBigInt(value: Object, replacer?: (number | string)[] | null, space?: string | number): string; 43} 44 45function parse(text: string, reviver?: TransformsFunc, options?: ParseOptions): Object | null { 46 if (typeof text !== 'string') { 47 let error = new BusinessError(`Parameter error. The type of ${text} must be string`); 48 throw error; 49 } 50 if (reviver) { 51 if (typeof reviver !== 'function') { 52 let error = new BusinessError(`Parameter error. The type of ${reviver} must be a method`); 53 throw error; 54 } 55 } 56 57 try { 58 return (JSON as unknown as JSON).parseBigInt(text, reviver, options); 59 } catch (e) { 60 let error = new BusinessError(`e.message: ` + (e as Error).message + `, e.name: ` + (e as Error).name); 61 throw error; 62 } 63} 64 65function stringfyFun(value: Object, replacer?: (this: Object, key: string, value: Object) => Object, 66 space?: string | number): string { 67 if (arguments.length === 1) { 68 return JSON.stringify(value); 69 } else { 70 return JSON.stringify(value, replacer, space); 71 } 72} 73 74function stringfyArr(value: Object, replacer?: (number | string)[] | null, space?: string | number): string { 75 if (arguments.length === 1) { 76 return (JSON as unknown as JSON).stringifyBigInt(value); 77 } else { 78 return (JSON as unknown as JSON).stringifyBigInt(value, replacer, space); 79 } 80} 81 82function isParameterType(self: unknown): boolean { 83 return (Array.isArray(self) && self.every((item) => typeof item === 'number' || typeof item === 'string') || 84 self === null || typeof self === 'function'); 85} 86 87function isSpaceType(self: unknown): boolean { 88 return (typeof self === 'string' || typeof self === 'number'); 89} 90 91function isCirculateReference(value: Object, seenObjects: Set<Object> = new Set()): boolean { 92 if (seenObjects.has(value)) { 93 return true; 94 } 95 seenObjects.add(value); 96 if (value === null || typeof value !== 'object') { 97 return false; 98 } 99 for (const key in value) { 100 if (Object.prototype.hasOwnProperty.call(value, key)) { 101 const temp = value[key]; 102 if (temp !== null && typeof temp === 'object' && 103 (seenObjects.has(temp) || isCirculateReference(temp, seenObjects))) { 104 return true; 105 } 106 } 107 } 108 seenObjects.delete(value); 109 return false; 110} 111 112function stringify(value: Object, replacer?: ReplacerType, space?: string | number): string { 113 if (isCirculateReference(value)) { 114 let error = new BusinessError(`Parameter error. The object circular Reference`); 115 throw error; 116 } 117 if (replacer) { 118 if (!isParameterType(replacer)) { 119 let error = new BusinessError(`Parameter error. The type of ${replacer} must be a method or array`); 120 throw error; 121 } 122 if (space) { 123 if (!isSpaceType(space)) { 124 let error = new BusinessError(`Parameter error. The type of ${space} must be a string or number`); 125 throw error; 126 } 127 } else if (space === null) { 128 let error = new BusinessError(`Parameter error. The type of ${space} must be a string or number`); 129 throw error; 130 } 131 } 132 133 try { 134 if (arguments.length === 1) { 135 return stringfyArr(value); 136 } else { 137 if (typeof replacer === 'function') { 138 return stringfyFun(value, replacer, space); 139 } else { 140 return stringfyArr(value, replacer, space); 141 } 142 } 143 } catch (e) { 144 let error = new BusinessError((e as Error).message + `, e.name: ` + (e as Error).name); 145 throw error; 146 } 147} 148 149function has(value: object, key: string): boolean { 150 if (typeof value !== 'object' || typeof value === 'undefined' || value === null) { 151 let error = new BusinessError(`Parameter error. The type of ${value} must be object`); 152 throw error; 153 } 154 if (value instanceof Array) { 155 let error = new BusinessError(`Parameter error. The type of ${value} must be json object`); 156 throw error; 157 } 158 if (!(typeof key === 'string' && key.length)) { 159 let error = new BusinessError(`Parameter error. The type of ${key} must be string and not empty`); 160 throw error; 161 } 162 return Object.prototype.hasOwnProperty.call(value, key); 163} 164 165function remove(value: object, key: string): void { 166 if (typeof value !== 'object' || typeof value === 'undefined' || value === null) { 167 let error = new BusinessError(`Parameter error. The type of ${value} must be object`); 168 throw error; 169 } 170 if (value instanceof Array) { 171 let error = new BusinessError(`Parameter error. The type of ${value} must be json object`); 172 throw error; 173 } 174 if (!(typeof key === 'string' && key.length)) { 175 let error = new BusinessError(`Parameter error. The type of ${key} must be string and not empty`); 176 throw error; 177 } 178 if (Object.prototype.hasOwnProperty.call(value, key)) { 179 delete value[key]; 180 } 181} 182 183export default { 184 parse: parse, 185 stringify: stringify, 186 has: has, 187 remove: remove, 188}; 189