@socketsecurity/lib
Version:
Core utilities and infrastructure for Socket.dev security tools
520 lines (519 loc) • 17.6 kB
TypeScript
// Type definitions
/**
* Record of property keys mapped to getter functions.
* Used for defining lazy getters on objects.
*/
type GetterDefObj = {
[key: PropertyKey]: () => unknown;
};
/**
* Statistics tracking for lazy getter initialization.
* Keeps track of which lazy getters have been accessed and initialized.
*/
type LazyGetterStats = {
initialized?: Set<PropertyKey> | undefined;
};
/**
* Configuration options for creating constants objects.
*/
type ConstantsObjectOptions = {
/**
* Lazy getter definitions to attach to the object.
* @default undefined
*/
getters?: GetterDefObj | undefined;
/**
* Internal properties to store under `kInternalsSymbol`.
* @default undefined
*/
internals?: object | undefined;
/**
* Properties to mix into the object (lower priority than `props`).
* @default undefined
*/
mixin?: object | undefined;
};
/**
* Type helper that creates a remapped type with fresh property mapping.
* Useful for flattening intersection types into a single object type.
*/
type Remap<T> = {
[K in keyof T]: T[K];
} extends infer O ? {
[K in keyof O]: O[K];
} : never;
/**
* Type for generic property bag.
*/
type PropertyBag = {
[key: PropertyKey]: unknown;
};
/**
* Type for generic sorted object entries.
*/
type SortedObject<T> = {
[key: PropertyKey]: T;
};
export type { GetterDefObj, LazyGetterStats, ConstantsObjectOptions, Remap };
/**
* Create a lazy getter function that memoizes its result.
*
* The returned function will only call the getter once, caching the result
* for subsequent calls. This is useful for expensive computations or
* operations that should only happen when needed.
*
* @param name - The property key name for the getter (used for debugging and stats)
* @param getter - Function that computes the value on first access
* @param stats - Optional stats object to track initialization
* @returns A memoized getter function
*
* @example
* ```ts
* const stats = { initialized: new Set() }
* const getLargeData = createLazyGetter('data', () => {
* console.log('Computing expensive data...')
* return { large: 'dataset' }
* }, stats)
*
* getLargeData() // Logs "Computing expensive data..." and returns data
* getLargeData() // Returns cached data without logging
* console.log(stats.initialized.has('data')) // true
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function createLazyGetter<T>(name: PropertyKey, getter: () => T, stats?: LazyGetterStats | undefined): () => T;
/**
* Create a frozen constants object with lazy getters and internal properties.
*
* This function creates an immutable object with:
* - Regular properties from `props`
* - Lazy getters that compute values on first access
* - Internal properties accessible via `kInternalsSymbol`
* - Mixin properties (lower priority, won't override existing)
* - Alphabetically sorted keys for consistency
*
* The resulting object is deeply frozen and cannot be modified.
*
* @param props - Regular properties to include on the object
* @param options_ - Configuration options
* @returns A frozen object with all specified properties
*
* @example
* ```ts
* const config = createConstantsObject(
* { apiUrl: 'https://api.example.com' },
* {
* getters: {
* client: () => new APIClient(),
* timestamp: () => Date.now()
* },
* internals: {
* version: '1.0.0'
* }
* }
* )
*
* console.log(config.apiUrl) // 'https://api.example.com'
* console.log(config.client) // APIClient instance (computed on first access)
* console.log(config[kInternalsSymbol].version) // '1.0.0'
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function createConstantsObject(props: object, options_?: ConstantsObjectOptions | undefined): Readonly<object>;
/**
* Define a getter property on an object.
*
* The getter is non-enumerable and configurable, meaning it won't show up
* in `for...in` loops or `Object.keys()`, but can be redefined later.
*
* @param object - The object to define the getter on
* @param propKey - The property key for the getter
* @param getter - Function that computes the property value
* @returns The modified object (for chaining)
*
* @example
* ```ts
* const obj = {}
* defineGetter(obj, 'timestamp', () => Date.now())
* console.log(obj.timestamp) // Current timestamp
* console.log(obj.timestamp) // Different timestamp (computed each time)
* console.log(Object.keys(obj)) // [] (non-enumerable)
* ```
*/
export declare function defineGetter<T>(object: object, propKey: PropertyKey, getter: () => T): object;
/**
* Define a lazy getter property on an object.
*
* Unlike `defineGetter()`, this version memoizes the result so the getter
* function is only called once. Subsequent accesses return the cached value.
*
* @param object - The object to define the lazy getter on
* @param propKey - The property key for the lazy getter
* @param getter - Function that computes the value on first access
* @param stats - Optional stats object to track initialization
* @returns The modified object (for chaining)
*
* @example
* ```ts
* const obj = {}
* defineLazyGetter(obj, 'data', () => {
* console.log('Loading data...')
* return { expensive: 'computation' }
* })
* console.log(obj.data) // Logs "Loading data..." and returns data
* console.log(obj.data) // Returns same data without logging
* ```
*/
export declare function defineLazyGetter<T>(object: object, propKey: PropertyKey, getter: () => T, stats?: LazyGetterStats | undefined): object;
/**
* Define multiple lazy getter properties on an object.
*
* Each getter in the provided object will be converted to a lazy getter
* and attached to the target object. All getters share the same stats object
* for tracking initialization.
*
* @param object - The object to define lazy getters on
* @param getterDefObj - Object mapping property keys to getter functions
* @param stats - Optional stats object to track initialization
* @returns The modified object (for chaining)
*
* @example
* ```ts
* const obj = {}
* const stats = { initialized: new Set() }
* defineLazyGetters(obj, {
* user: () => fetchUser(),
* config: () => loadConfig(),
* timestamp: () => Date.now()
* }, stats)
*
* console.log(obj.user) // Fetches user on first access
* console.log(obj.config) // Loads config on first access
* console.log(stats.initialized) // Set(['user', 'config'])
* ```
*/
export declare function defineLazyGetters(object: object, getterDefObj: GetterDefObj | undefined, stats?: LazyGetterStats | undefined): object;
/**
* Compare two entry arrays by their keys for sorting.
*
* Used internally for alphabetically sorting object entries.
* String keys are compared directly, non-string keys are converted to strings first.
*
* @param a - First entry tuple [key, value]
* @param b - Second entry tuple [key, value]
* @returns Negative if a < b, positive if a > b, zero if equal
*
* @example
* ```ts
* const entries = [['zebra', 1], ['apple', 2], ['banana', 3]]
* entries.sort(entryKeyComparator)
* // [['apple', 2], ['banana', 3], ['zebra', 1]]
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function entryKeyComparator(a: [PropertyKey, unknown], b: [PropertyKey, unknown]): number;
/**
* Get the enumerable own property keys of an object.
*
* This is a safe wrapper around `Object.keys()` that returns an empty array
* for non-object values instead of throwing an error.
*
* @param obj - The value to get keys from
* @returns Array of enumerable string keys, or empty array for non-objects
*
* @example
* ```ts
* getKeys({ a: 1, b: 2 }) // ['a', 'b']
* getKeys([10, 20, 30]) // ['0', '1', '2']
* getKeys(null) // []
* getKeys(undefined) // []
* getKeys('hello') // []
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function getKeys(obj: unknown): string[];
/**
* Get an own property value from an object safely.
*
* Returns `undefined` if the value is null/undefined or if the property
* doesn't exist as an own property (not inherited). This avoids prototype
* chain lookups and prevents errors on null/undefined values.
*
* @param obj - The object to get the property from
* @param propKey - The property key to look up
* @returns The property value, or `undefined` if not found or obj is null/undefined
*
* @example
* ```ts
* const obj = { name: 'Alice', age: 30 }
* getOwn(obj, 'name') // 'Alice'
* getOwn(obj, 'missing') // undefined
* getOwn(obj, 'toString') // undefined (inherited, not own property)
* getOwn(null, 'name') // undefined
* getOwn(undefined, 'name') // undefined
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function getOwn(obj: unknown, propKey: PropertyKey): unknown;
/**
* Get all own property values from an object.
*
* Returns values for all own properties (enumerable and non-enumerable),
* but not inherited properties. Returns an empty array for null/undefined.
*
* @param obj - The object to get values from
* @returns Array of all own property values, or empty array for null/undefined
*
* @example
* ```ts
* getOwnPropertyValues({ a: 1, b: 2, c: 3 }) // [1, 2, 3]
* getOwnPropertyValues([10, 20, 30]) // [10, 20, 30]
* getOwnPropertyValues(null) // []
* getOwnPropertyValues(undefined) // []
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function getOwnPropertyValues<T>(obj: {
[key: PropertyKey]: T;
} | null | undefined): T[];
/**
* Check if an object has any enumerable own properties.
*
* Returns `true` if the object has at least one enumerable own property,
* `false` otherwise. Also returns `false` for null/undefined.
*
* @param obj - The value to check
* @returns `true` if obj has enumerable own properties, `false` otherwise
*
* @example
* ```ts
* hasKeys({ a: 1 }) // true
* hasKeys({}) // false
* hasKeys([]) // false
* hasKeys([1, 2]) // true
* hasKeys(null) // false
* hasKeys(undefined) // false
* hasKeys(Object.create({ inherited: true })) // false (inherited, not own)
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function hasKeys(obj: unknown): obj is PropertyBag;
/**
* Check if an object has an own property.
*
* Type-safe wrapper around `Object.hasOwn()` that returns `false` for
* null/undefined instead of throwing. Only checks own properties, not
* inherited ones from the prototype chain.
*
* @param obj - The value to check
* @param propKey - The property key to look for
* @returns `true` if obj has the property as an own property, `false` otherwise
*
* @example
* ```ts
* const obj = { name: 'Alice' }
* hasOwn(obj, 'name') // true
* hasOwn(obj, 'age') // false
* hasOwn(obj, 'toString') // false (inherited from Object.prototype)
* hasOwn(null, 'name') // false
* hasOwn(undefined, 'name') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function hasOwn(obj: unknown, propKey: PropertyKey): obj is object & PropertyBag;
/**
* Check if a value is an object (including arrays).
*
* Returns `true` for any object type including arrays, functions, dates, etc.
* Returns `false` for primitives and `null`.
*
* @param value - The value to check
* @returns `true` if value is an object (including arrays), `false` otherwise
*
* @example
* ```ts
* isObject({}) // true
* isObject([]) // true
* isObject(new Date()) // true
* isObject(() => {}) // false (functions are not objects for typeof)
* isObject(null) // false
* isObject(undefined) // false
* isObject(42) // false
* isObject('string') // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function isObject(value: unknown): value is {
[key: PropertyKey]: unknown;
};
/**
* Check if a value is a plain object (not an array, not a built-in).
*
* Returns `true` only for plain objects created with `{}` or `Object.create(null)`.
* Returns `false` for arrays, built-in objects (Date, RegExp, etc.), and primitives.
*
* @param value - The value to check
* @returns `true` if value is a plain object, `false` otherwise
*
* @example
* ```ts
* isObjectObject({}) // true
* isObjectObject({ a: 1 }) // true
* isObjectObject(Object.create(null)) // true
* isObjectObject([]) // false
* isObjectObject(new Date()) // false
* isObjectObject(null) // false
* isObjectObject(42) // false
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function isObjectObject(value: unknown): value is {
[key: PropertyKey]: unknown;
};
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
// `exports.SomeName = void 0;` which causes runtime errors.
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
/**
* Alias for native `Object.assign`.
*
* Copies all enumerable own properties from one or more source objects
* to a target object and returns the modified target object.
*
* @example
* ```ts
* const target = { a: 1 }
* const source = { b: 2, c: 3 }
* objectAssign(target, source) // { a: 1, b: 2, c: 3 }
* ```
*/
export declare const objectAssign: {
<T extends {}, U>(target: T, source: U): T & U;
<T extends {}, U, V>(target: T, source1: U, source2: V): T & U & V;
<T extends {}, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W;
(target: object, ...sources: any[]): any;
};
/**
* Get all own property entries (key-value pairs) from an object.
*
* Unlike `Object.entries()`, this includes non-enumerable properties and
* symbol keys. Returns an empty array for null/undefined.
*
* @param obj - The object to get entries from
* @returns Array of [key, value] tuples, or empty array for null/undefined
*
* @example
* ```ts
* objectEntries({ a: 1, b: 2 }) // [['a', 1], ['b', 2]]
* const sym = Symbol('key')
* objectEntries({ [sym]: 'value', x: 10 }) // [[Symbol(key), 'value'], ['x', 10]]
* objectEntries(null) // []
* objectEntries(undefined) // []
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function objectEntries(obj: unknown): Array<[PropertyKey, unknown]>;
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
// `exports.SomeName = void 0;` which causes runtime errors.
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
/**
* Alias for native `Object.freeze`.
*
* Freezes an object, preventing new properties from being added and existing
* properties from being removed or modified. Makes the object immutable.
*
* @example
* ```ts
* const obj = { a: 1 }
* objectFreeze(obj)
* obj.a = 2 // Silently fails in non-strict mode, throws in strict mode
* obj.b = 3 // Silently fails in non-strict mode, throws in strict mode
* ```
*/
export declare const objectFreeze: {
<T extends Function>(f: T): T;
<T extends {
[idx: string]: object | U;
}, U extends string | number | bigint | symbol | boolean>(o: T): Readonly<T>;
<T>(o: T): Readonly<T>;
};
/**
* Deep merge source object into target object.
*
* Recursively merges properties from `source` into `target`. Arrays in source
* completely replace arrays in target (no element-wise merging). Objects are
* merged recursively. Includes infinite loop detection for safety.
*
* @param target - The object to merge into (will be modified)
* @param source - The object to merge from
* @returns The modified target object
*
* @example
* ```ts
* const target = { a: { x: 1 }, b: [1, 2] }
* const source = { a: { y: 2 }, b: [3, 4, 5], c: 3 }
* merge(target, source)
* // { a: { x: 1, y: 2 }, b: [3, 4, 5], c: 3 }
* ```
*
* @example
* ```ts
* // Arrays are replaced, not merged
* merge({ arr: [1, 2] }, { arr: [3] }) // { arr: [3] }
*
* // Deep object merging
* merge(
* { config: { api: 'v1', timeout: 1000 } },
* { config: { api: 'v2', retries: 3 } }
* )
* // { config: { api: 'v2', timeout: 1000, retries: 3 } }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function merge<T extends object, U extends object>(target: T, source: U): T & U;
/**
* Convert an object to a new object with sorted keys.
*
* Creates a new object with the same properties as the input, but with keys
* sorted alphabetically. Symbol keys are sorted separately and placed first.
* This is useful for consistent key ordering in serialization or comparisons.
*
* @param obj - The object to sort
* @returns A new object with sorted keys
*
* @example
* ```ts
* toSortedObject({ z: 1, a: 2, m: 3 })
* // { a: 2, m: 3, z: 1 }
*
* const sym1 = Symbol('first')
* const sym2 = Symbol('second')
* toSortedObject({ z: 1, [sym2]: 2, a: 3, [sym1]: 4 })
* // { [Symbol(first)]: 4, [Symbol(second)]: 2, a: 3, z: 1 }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function toSortedObject<T extends object>(obj: T): T;
/**
* Create an object from entries with sorted keys.
*
* Takes an iterable of [key, value] entries and creates a new object with
* keys sorted alphabetically. Symbol keys are sorted separately and placed
* first in the resulting object.
*
* @param entries - Iterable of [key, value] tuples
* @returns A new object with sorted keys
*
* @example
* ```ts
* toSortedObjectFromEntries([['z', 1], ['a', 2], ['m', 3]])
* // { a: 2, m: 3, z: 1 }
*
* const entries = new Map([['beta', 2], ['alpha', 1], ['gamma', 3]])
* toSortedObjectFromEntries(entries)
* // { alpha: 1, beta: 2, gamma: 3 }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export declare function toSortedObjectFromEntries<T = unknown>(entries: Iterable<[PropertyKey, T]>): SortedObject<T>;