Serializer.ts (2908B)
1 /** 2 * @license 3 * Copyright 2023 Google Inc. 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; 8 9 import {isDate, isPlainObject, isRegExp} from '../common/util.js'; 10 11 /** 12 * @internal 13 */ 14 class UnserializableError extends Error {} 15 16 /** 17 * @internal 18 */ 19 export class BidiSerializer { 20 static serialize(arg: unknown): Bidi.Script.LocalValue { 21 switch (typeof arg) { 22 case 'symbol': 23 case 'function': 24 throw new UnserializableError(`Unable to serializable ${typeof arg}`); 25 case 'object': 26 return this.#serializeObject(arg); 27 28 case 'undefined': 29 return { 30 type: 'undefined', 31 }; 32 case 'number': 33 return this.#serializeNumber(arg); 34 case 'bigint': 35 return { 36 type: 'bigint', 37 value: arg.toString(), 38 }; 39 case 'string': 40 return { 41 type: 'string', 42 value: arg, 43 }; 44 case 'boolean': 45 return { 46 type: 'boolean', 47 value: arg, 48 }; 49 } 50 } 51 52 static #serializeNumber(arg: number): Bidi.Script.LocalValue { 53 let value: Bidi.Script.SpecialNumber | number; 54 if (Object.is(arg, -0)) { 55 value = '-0'; 56 } else if (Object.is(arg, Infinity)) { 57 value = 'Infinity'; 58 } else if (Object.is(arg, -Infinity)) { 59 value = '-Infinity'; 60 } else if (Object.is(arg, NaN)) { 61 value = 'NaN'; 62 } else { 63 value = arg; 64 } 65 return { 66 type: 'number', 67 value, 68 }; 69 } 70 71 static #serializeObject(arg: object | null): Bidi.Script.LocalValue { 72 if (arg === null) { 73 return { 74 type: 'null', 75 }; 76 } else if (Array.isArray(arg)) { 77 const parsedArray = arg.map(subArg => { 78 return this.serialize(subArg); 79 }); 80 81 return { 82 type: 'array', 83 value: parsedArray, 84 }; 85 } else if (isPlainObject(arg)) { 86 try { 87 JSON.stringify(arg); 88 } catch (error) { 89 if ( 90 error instanceof TypeError && 91 error.message.startsWith('Converting circular structure to JSON') 92 ) { 93 error.message += ' Recursive objects are not allowed.'; 94 } 95 throw error; 96 } 97 98 const parsedObject: Bidi.Script.MappingLocalValue = []; 99 for (const key in arg) { 100 parsedObject.push([this.serialize(key), this.serialize(arg[key])]); 101 } 102 103 return { 104 type: 'object', 105 value: parsedObject, 106 }; 107 } else if (isRegExp(arg)) { 108 return { 109 type: 'regexp', 110 value: { 111 pattern: arg.source, 112 flags: arg.flags, 113 }, 114 }; 115 } else if (isDate(arg)) { 116 return { 117 type: 'date', 118 value: arg.toISOString(), 119 }; 120 } 121 122 throw new UnserializableError( 123 'Custom object serialization not possible. Use plain objects instead.', 124 ); 125 } 126 }