General-purpose utility functions for object manipulation, environment detection, error handling, and data processing.
Creates deep copies of objects and arrays, handling circular references and preserving prototypes.
/**
* Deeply clone a value to create a new instance
* @param value - Value to clone
* @returns Deep copy of the input value
*/
function cloneDeep<T>(value: T): T;Usage Examples:
import { cloneDeep } from "apollo-utilities";
// Clone simple objects
const original = { name: "John", age: 30, hobbies: ["reading", "gaming"] };
const cloned = cloneDeep(original);
cloned.name = "Jane";
cloned.hobbies.push("cooking");
console.log(original.name); // "John" (unchanged)
console.log(original.hobbies); // ["reading", "gaming"] (unchanged)
console.log(cloned.name); // "Jane"
console.log(cloned.hobbies); // ["reading", "gaming", "cooking"]
// Clone nested objects
const complex = {
user: {
profile: {
settings: {
theme: "dark",
notifications: true
}
}
},
posts: [
{ title: "Post 1", tags: ["tech"] },
{ title: "Post 2", tags: ["life"] }
]
};
const clonedComplex = cloneDeep(complex);
clonedComplex.user.profile.settings.theme = "light";
console.log(complex.user.profile.settings.theme); // "dark" (unchanged)
// Handles circular references
const circular = { name: "test" };
circular.self = circular;
const clonedCircular = cloneDeep(circular);
console.log(clonedCircular.self === clonedCircular); // true
console.log(clonedCircular === circular); // falseDeep equality comparison function re-exported from @wry/equality.
/**
* Deep equality comparison (re-exported from @wry/equality)
* @param a - First value to compare
* @param b - Second value to compare
* @returns true if values are deeply equal
*/
function isEqual(a: any, b: any): boolean;Usage Examples:
import { isEqual } from "apollo-utilities";
// Primitive values
console.log(isEqual(42, 42)); // true
console.log(isEqual("hello", "hello")); // true
console.log(isEqual(true, false)); // false
// Objects
const obj1 = { name: "John", age: 30, hobbies: ["reading"] };
const obj2 = { name: "John", age: 30, hobbies: ["reading"] };
const obj3 = { name: "Jane", age: 30, hobbies: ["reading"] };
console.log(isEqual(obj1, obj2)); // true (same content)
console.log(isEqual(obj1, obj3)); // false (different name)
console.log(obj1 === obj2); // false (different references)
// Arrays
console.log(isEqual([1, 2, 3], [1, 2, 3])); // true
console.log(isEqual([1, 2, 3], [3, 2, 1])); // false (different order)
// Nested structures
const nested1 = { user: { profile: { name: "John" } } };
const nested2 = { user: { profile: { name: "John" } } };
console.log(isEqual(nested1, nested2)); // trueObject.assign polyfill with multiple overloads for type safety.
/**
* Merge properties from source objects into target object
* @param target - Target object to merge into
* @param sources - Source objects to merge from
* @returns Target object with merged properties
*/
function assign<A, B>(a: A, b: B): A & B;
function assign<A, B, C>(a: A, b: B, c: C): A & B & C;
function assign<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D;
function assign<A, B, C, D, E>(a: A, b: B, c: C, d: D, e: E): A & B & C & D & E;
function assign(target: any, ...sources: Array<any>): any;Usage Examples:
import { assign } from "apollo-utilities";
// Basic assignment
const target = { name: "John" };
const source = { age: 30, city: "New York" };
const result = assign(target, source);
console.log(result); // { name: "John", age: 30, city: "New York" }
console.log(target === result); // true (target is modified)
// Multiple sources
const base = { a: 1 };
const ext1 = { b: 2 };
const ext2 = { c: 3 };
const merged = assign(base, ext1, ext2);
console.log(merged); // { a: 1, b: 2, c: 3 }
// Property override
const obj1 = { name: "John", age: 25 };
const obj2 = { age: 30, city: "Boston" };
const final = assign(obj1, obj2);
console.log(final); // { name: "John", age: 30, city: "Boston" }
// Handle null/undefined sources
const safe = assign({ a: 1 }, null, undefined, { b: 2 });
console.log(safe); // { a: 1, b: 2 }Advanced object merging with memory-efficient sharing and type inference.
/**
* Deep merge multiple objects with memory sharing
* @param sources - Objects to merge
* @returns Merged object with shared memory where possible
*/
function mergeDeep<T extends any[]>(...sources: T): TupleToIntersection<T>;
/**
* Deep merge array of objects
* @param sources - Array of objects to merge
* @returns Merged object
*/
function mergeDeepArray<T>(sources: T[]): T;Usage Examples:
import { mergeDeep, mergeDeepArray } from "apollo-utilities";
// Basic deep merge
const base = {
user: {
name: "John",
settings: {
theme: "dark",
notifications: true
}
}
};
const update = {
user: {
age: 30,
settings: {
language: "en"
}
}
};
const merged = mergeDeep(base, update);
console.log(merged);
// {
// user: {
// name: "John", // preserved from base
// age: 30, // added from update
// settings: {
// theme: "dark", // preserved from base
// notifications: true, // preserved from base
// language: "en" // added from update
// }
// }
// }
// Multiple objects
const config1 = { api: { timeout: 5000 } };
const config2 = { api: { retries: 3 } };
const config3 = { ui: { theme: "light" } };
const finalConfig = mergeDeep(config1, config2, config3);
console.log(finalConfig);
// {
// api: { timeout: 5000, retries: 3 },
// ui: { theme: "light" }
// }
// Array version
const configs = [config1, config2, config3];
const arrayMerged = mergeDeepArray(configs);
console.log(isEqual(finalConfig, arrayMerged)); // trueFunctions for detecting the current runtime environment.
/**
* Get current environment from NODE_ENV
* @returns Environment string or "development" as default
*/
function getEnv(): string | undefined;
/**
* Check if current environment matches specified environment
* @param env - Environment name to check
* @returns true if current environment matches
*/
function isEnv(env: string): boolean;
/**
* Check if running in production environment
* @returns true if NODE_ENV is "production"
*/
function isProduction(): boolean;
/**
* Check if running in development environment
* @returns true if NODE_ENV is "development"
*/
function isDevelopment(): boolean;
/**
* Check if running in test environment
* @returns true if NODE_ENV is "test"
*/
function isTest(): boolean;Usage Examples:
import {
getEnv,
isProduction,
isDevelopment,
isTest,
isEnv
} from "apollo-utilities";
// Check current environment
console.log(getEnv()); // "development" (or current NODE_ENV)
// Conditional logic based on environment
if (isProduction()) {
// Production-only code
console.log("Running in production mode");
} else if (isDevelopment()) {
// Development-only code
console.log("Development mode - extra logging enabled");
}
if (isTest()) {
// Test-specific behavior
console.log("Running in test environment");
}
// Custom environment check
if (isEnv("staging")) {
console.log("Running in staging environment");
}
// Example usage in configuration
const config = {
apiUrl: isProduction()
? "https://api.production.com"
: "https://api.dev.com",
debug: isDevelopment(),
logLevel: isTest() ? "silent" : "info"
};Utilities for safe function execution and GraphQL result validation.
/**
* Execute function safely and log any errors
* @param f - Function to execute
* @returns Function result or undefined if error occurred
*/
function tryFunctionOrLogError(f: Function): any;
/**
* Check if GraphQL execution result has errors
* @param result - GraphQL execution result
* @returns true if result contains errors
*/
function graphQLResultHasError(result: ExecutionResult): boolean;Usage Examples:
import { tryFunctionOrLogError, graphQLResultHasError } from "apollo-utilities";
// Safe function execution
const riskyFunction = () => {
throw new Error("Something went wrong");
};
const safeFunction = () => {
return { success: true };
};
const result1 = tryFunctionOrLogError(riskyFunction);
console.log(result1); // undefined (error logged to console)
const result2 = tryFunctionOrLogError(safeFunction);
console.log(result2); // { success: true }
// GraphQL result validation
const successResult = {
data: { user: { name: "John" } }
};
const errorResult = {
data: null,
errors: [{ message: "User not found" }]
};
console.log(graphQLResultHasError(successResult)); // false
console.log(graphQLResultHasError(errorResult)); // true
// Use in GraphQL client code
async function executeQuery(query) {
const result = await client.query({ query });
if (graphQLResultHasError(result)) {
console.error("Query failed:", result.errors);
return null;
}
return result.data;
}Utility for displaying warnings only once in development environment.
/**
* Print warning once in development environment
* @param msg - Warning message to display
* @param type - Message type: "warn" or "error" (default: "warn")
*/
function warnOnceInDevelopment(msg: string, type?: string): void;Usage Examples:
import { warnOnceInDevelopment } from "apollo-utilities";
// Basic warning (only shows once in development)
function deprecatedFunction() {
warnOnceInDevelopment("deprecatedFunction is deprecated, use newFunction instead");
// Function implementation...
}
// Call multiple times - warning only appears once
deprecatedFunction(); // Warning logged
deprecatedFunction(); // No warning (already shown)
deprecatedFunction(); // No warning (already shown)
// Error-level warning
function riskyOperation() {
warnOnceInDevelopment(
"riskyOperation may cause data loss in certain conditions",
"error"
);
}
// Conditional warnings
function configValidation(config) {
if (!config.apiKey) {
warnOnceInDevelopment("API key not provided - using default configuration");
}
if (config.timeout < 1000) {
warnOnceInDevelopment("Timeout less than 1000ms may cause reliability issues");
}
}Additional utilities for data processing and development aids.
/**
* Remove symbols from data for testing purposes
* @param data - Data to process
* @returns Data with symbols removed (via JSON parse/stringify)
*/
function stripSymbols<T>(data: T): T;
/**
* Maybe deep freeze object in development/test environments
* @param obj - Object to potentially freeze
* @returns Frozen object in dev/test, original object in production
*/
function maybeDeepFreeze(obj: any): any;Usage Examples:
import { stripSymbols, maybeDeepFreeze } from "apollo-utilities";
// Strip symbols for testing
const dataWithSymbols = {
name: "test",
[Symbol.iterator]: function* () { yield 1; },
nested: {
value: 42,
[Symbol.for("key")]: "symbol-value"
}
};
const clean = stripSymbols(dataWithSymbols);
console.log(clean); // { name: "test", nested: { value: 42 } }
// Conditional deep freezing
const config = { api: { key: "secret" } };
const frozenConfig = maybeDeepFreeze(config);
// In development/test: throws error when trying to modify
// In production: allows modification
try {
frozenConfig.api.key = "new-secret";
} catch (error) {
console.log("Cannot modify frozen object"); // In dev/test only
}Utility for detecting platform capabilities.
/**
* Whether WeakMap can be used safely in current environment
*/
const canUseWeakMap: boolean;Usage Example:
import { canUseWeakMap } from "apollo-utilities";
// Use WeakMap only if supported and safe
const cache = canUseWeakMap
? new WeakMap()
: new Map(); // Fallback to Map
if (canUseWeakMap) {
console.log("Using WeakMap for caching");
} else {
console.log("WeakMap not available, using Map fallback");
}type TupleToIntersection<T extends any[]> =
T extends [infer A] ? A :
T extends [infer A, infer B] ? A & B :
T extends [infer A, infer B, infer C] ? A & B & C :
T extends [infer A, infer B, infer C, infer D] ? A & B & C & D :
T extends [infer A, infer B, infer C, infer D, infer E] ? A & B & C & D & E :
T extends (infer U)[] ? U : any;