UNPKG

@socketsecurity/lib

Version:

Core utilities and infrastructure for Socket.dev security tools

520 lines (519 loc) 17.6 kB
// 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>;