| 128 | } |
| 129 | |
| 130 | function normalize(value: unknown): unknown { |
| 131 | if (value instanceof Decimal) { |
| 132 | return { __decimal: value.toString() }; |
| 133 | } |
| 134 | if ( |
| 135 | value instanceof BoolArray || |
| 136 | value instanceof ForyFloat16Array || |
| 137 | value instanceof BFloat16Array |
| 138 | ) { |
| 139 | return Array.from(value as Iterable<unknown>, (item) => normalize(item)); |
| 140 | } |
| 141 | if (value instanceof Date) { |
| 142 | return { __dateMs: value.getTime() }; |
| 143 | } |
| 144 | if (value instanceof Map) { |
| 145 | const entries = Array.from(value.entries()).map( |
| 146 | ([key, itemValue]) => [normalize(key), normalize(itemValue)] as const, |
| 147 | ); |
| 148 | entries.sort((left, right) => |
| 149 | JSON.stringify(left[0]).localeCompare(JSON.stringify(right[0])), |
| 150 | ); |
| 151 | return entries; |
| 152 | } |
| 153 | if (value instanceof Set) { |
| 154 | return Array.from(value.values()) |
| 155 | .map((item) => normalize(item)) |
| 156 | .sort(); |
| 157 | } |
| 158 | if (ArrayBuffer.isView(value)) { |
| 159 | if (value instanceof DataView) { |
| 160 | return Array.from( |
| 161 | new Uint8Array(value.buffer, value.byteOffset, value.byteLength), |
| 162 | ); |
| 163 | } |
| 164 | return Array.from(value as unknown as ArrayLike<unknown>, (item) => |
| 165 | normalize(item), |
| 166 | ); |
| 167 | } |
| 168 | if (Array.isArray(value)) { |
| 169 | return value.map((item) => normalize(item)); |
| 170 | } |
| 171 | if (value != null && typeof value === "object") { |
| 172 | const entries = Object.entries(value as Record<string, unknown>); |
| 173 | entries.sort(([left], [right]) => left.localeCompare(right)); |
| 174 | return Object.fromEntries( |
| 175 | entries.map(([key, itemValue]) => [key, normalize(itemValue)]), |
| 176 | ); |
| 177 | } |
| 178 | return value; |
| 179 | } |
| 180 | |
| 181 | function expectAcyclicEqual(expected: unknown, actual: unknown): void { |
| 182 | expect(normalize(actual)).toEqual(normalize(expected)); |