UNPKG

chrome-devtools-frontend

Version:
117 lines (102 loc) 3.46 kB
// Copyright 2023 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. export const mod = (a: number, n: number): number => { return ((a % n) + n) % n; }; export function assert<T>( predicate: T, message = 'Assertion failed!', ): asserts predicate { if (!predicate) { throw new Error(message); } } export type Keys<T> = T extends T ? keyof T : never; export type RequiredKeys<T> = { [K in keyof T] -?: {} extends Pick<T, K>? never : K; }[keyof T]; export type OptionalKeys<T> = { [K in keyof T] -?: {} extends Pick<T, K>? K : never; }[keyof T]; export type DeepImmutable<T> = { readonly[K in keyof T]: DeepImmutable<T[K]>; }; export type DeepMutable<T> = { -readonly[K in keyof T]: DeepMutable<T[K]>; }; export type DeepPartial<T> = { [K in keyof T]?: DeepPartial<Exclude<T[K], undefined>>; }; export type Mutable<T> = { -readonly[K in keyof T]: T[K]; }; export const deepFreeze = <T extends object>(object: T): DeepImmutable<T> => { for (const name of Reflect.ownKeys(object)) { const value = object[name as keyof T]; if ((value && typeof value === 'object') || typeof value === 'function') { deepFreeze(value); } } return Object.freeze(object); }; export class InsertAssignment<T> { value: T; constructor(value: T) { this.value = value; } } export class ArrayAssignments<T> { value: {[n: number]: T}; constructor(value: {[n: number]: T}) { this.value = value; } } export type Assignments<T> = T extends Readonly<Array<infer R>>? R[]|ArrayAssignments<Assignments<R>|InsertAssignment<R>>: {[K in keyof T]: Assignments<T[K]>}; export const immutableDeepAssign = <T>( object: DeepImmutable<T>, assignments: DeepImmutable<DeepPartial<Assignments<T>>>, ): DeepImmutable<T> => { if (assignments instanceof ArrayAssignments) { assert(Array.isArray(object), `Expected an array. Got ${typeof object}.`); const updatedObject = [...object] as Mutable<typeof object>; const keys = Object.keys(assignments.value) .sort( (a, b) => Number(b) - Number(a), ) as (keyof typeof updatedObject)[]; for (const key of keys) { const update = assignments.value[Number(key)]; if (update === undefined) { updatedObject.splice(Number(key), 1); } else if (update instanceof InsertAssignment) { updatedObject.splice(Number(key), 0, update.value); } else { updatedObject[Number(key)] = immutableDeepAssign( updatedObject[key], update, ); } } return Object.freeze(updatedObject); } if (typeof assignments === 'object' && !Array.isArray(assignments)) { assert(!Array.isArray(object), 'Expected an object. Got an array.'); const updatedObject = {...object} as Mutable<typeof object>; const keys = Object.keys(assignments) as (keyof typeof assignments&keyof typeof updatedObject)[]; for (const key of keys) { const update = assignments[key]; if (update === undefined) { delete updatedObject[key]; } else { updatedObject[key] = immutableDeepAssign( updatedObject[key], update as typeof updatedObject[typeof key], ); } } return Object.freeze(updatedObject); } return assignments as DeepImmutable<T>; };