Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@leather.io/utils

Package Overview
Dependencies
Maintainers
1
Versions
121
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@leather.io/utils - npm Package Compare versions

Comparing version
0.43.0
to
0.44.0
+263
src/flatten-object.spec.ts
import { flattenObject } from './flatten-object';
describe(flattenObject.name, () => {
describe('Object flattening', () => {
test('flattens simple nested objects', () => {
const input = { layer1: { layer2: 'value' } };
const expected = { 'layer1.layer2': 'value' };
expect(flattenObject(input)).toEqual(expected);
});
test('flattens deeply nested objects', () => {
const input = {
level1: {
level2: {
level3: {
level4: 'deep value',
},
},
},
};
const expected = { 'level1.level2.level3.level4': 'deep value' };
expect(flattenObject(input)).toEqual(expected);
});
test('flattens objects with multiple properties', () => {
const input = {
user: {
name: 'John',
age: 30,
address: {
street: '123 Main St',
city: 'Anytown',
},
},
status: 'active',
};
const expected = {
'user.name': 'John',
'user.age': 30,
'user.address.street': '123 Main St',
'user.address.city': 'Anytown',
status: 'active',
};
expect(flattenObject(input)).toEqual(expected);
});
test('handles empty objects', () => {
expect(flattenObject({})).toEqual({});
});
});
describe('Array flattening', () => {
test('flattens simple arrays with objects', () => {
const input = [{ key: 'value' }];
const expected = { '[0].key': 'value' };
expect(flattenObject(input)).toEqual(expected);
});
test('flattens arrays with multiple objects', () => {
const input = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
];
const expected = {
'[0].name': 'John',
'[0].age': 30,
'[1].name': 'Jane',
'[1].age': 25,
};
expect(flattenObject(input)).toEqual(expected);
});
test('flattens arrays with primitive values', () => {
const input = ['first', 'second', 'third'];
const expected = {
'[0]': 'first',
'[1]': 'second',
'[2]': 'third',
};
expect(flattenObject(input)).toEqual(expected);
});
test('flattens nested arrays', () => {
const input = [
[1, 2],
[3, 4],
];
const expected = {
'[0][0]': 1,
'[0][1]': 2,
'[1][0]': 3,
'[1][1]': 4,
};
expect(flattenObject(input)).toEqual(expected);
});
test('handles empty arrays', () => {
expect(flattenObject([])).toEqual({});
});
});
describe('Mixed object and array flattening', () => {
test('flattens objects containing arrays', () => {
const input = {
users: [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
],
meta: {
count: 2,
},
};
const expected = {
'users[0].name': 'John',
'users[0].age': 30,
'users[1].name': 'Jane',
'users[1].age': 25,
'meta.count': 2,
};
expect(flattenObject(input)).toEqual(expected);
});
test('flattens arrays containing objects with nested arrays', () => {
const input = [
{
id: 1,
tags: ['tag1', 'tag2'],
},
{
id: 2,
tags: ['tag3'],
},
];
const expected = {
'[0].id': 1,
'[0].tags[0]': 'tag1',
'[0].tags[1]': 'tag2',
'[1].id': 2,
'[1].tags[0]': 'tag3',
};
expect(flattenObject(input)).toEqual(expected);
});
test('handles complex nested structures', () => {
const input = {
data: {
items: [
{
name: 'item1',
properties: {
color: 'red',
sizes: ['small', 'medium'],
},
},
],
},
};
const expected = {
'data.items[0].name': 'item1',
'data.items[0].properties.color': 'red',
'data.items[0].properties.sizes[0]': 'small',
'data.items[0].properties.sizes[1]': 'medium',
};
expect(flattenObject(input)).toEqual(expected);
});
});
describe('Primitive value handling', () => {
test('handles null values', () => {
const input = { key: null };
const expected = { key: null };
expect(flattenObject(input)).toEqual(expected);
});
test('handles boolean values', () => {
const input = { active: true, disabled: false };
const expected = { active: true, disabled: false };
expect(flattenObject(input)).toEqual(expected);
});
test('handles number values', () => {
const input = {
integer: 42,
float: 3.14,
zero: 0,
negative: -10,
};
const expected = {
integer: 42,
float: 3.14,
zero: 0,
negative: -10,
};
expect(flattenObject(input)).toEqual(expected);
});
test('handles string values', () => {
const input = {
text: 'hello world',
empty: '',
unicode: '🚀',
};
const expected = {
text: 'hello world',
empty: '',
unicode: '🚀',
};
expect(flattenObject(input)).toEqual(expected);
});
test('handles root primitive values', () => {
expect(flattenObject('hello')).toEqual({ value: 'hello' });
expect(flattenObject(42)).toEqual({ value: 42 });
expect(flattenObject(true)).toEqual({ value: true });
expect(flattenObject(null)).toEqual({ value: null });
});
});
describe('Edge cases', () => {
test('handles objects with array-like property names', () => {
const input = {
'0': 'first',
'1': 'second',
length: 2,
};
const expected = {
'0': 'first',
'1': 'second',
length: 2,
};
expect(flattenObject(input)).toEqual(expected);
});
test('handles keys with special characters', () => {
const input = {
'key-with-dash': 'value1',
key_with_underscore: 'value2',
'key with space': 'value3',
'key.with.dot': 'value4',
};
const expected = {
'key-with-dash': 'value1',
key_with_underscore: 'value2',
'key with space': 'value3',
'key.with.dot': 'value4',
};
expect(flattenObject(input)).toEqual(expected);
});
test('handles sparse arrays', () => {
const input: any[] = [];
input[0] = 'first';
input[2] = 'third';
const result = flattenObject(input);
expect(result).toEqual({
'[0]': 'first',
'[1]': undefined,
'[2]': 'third',
});
});
});
});
type ObjectValue = string | number | boolean | null | NestedObject | ObjectValue[];
interface NestedObject {
[key: string]: ObjectValue;
}
interface FlattenedObject {
[key: string]: string | number | boolean | null;
}
/**
* Flattens a nested object or array into a flat object with dot-notation key paths.
*
* @param input - The object or array to flatten
* @param prefix - Internal parameter for recursion, represents the current key path
* @returns A flat object where keys represent the path to the original nested value
*
* @example
* ```typescript
* // Object example
* flattenObject({ layer1: { layer2: 'value' } })
* // Returns: { 'layer1.layer2': 'value' }
*
* // Array example
* flattenObject([{ key: 'value' }])
* // Returns: { '[0].key': 'value' }
*
* // Mixed example
* flattenObject({ users: [{ name: 'John', age: 30 }] })
* // Returns: { 'users[0].name': 'John', 'users[0].age': 30 }
* ```
*/
export function flattenObject(input: ObjectValue, prefix = ''): FlattenedObject {
const result: FlattenedObject = {};
if (input === null || typeof input !== 'object') {
if (prefix === '') return { value: input };
return { [prefix]: input };
}
if (Array.isArray(input)) {
input.forEach((item, index) => {
const key = prefix === '' ? `[${index}]` : `${prefix}[${index}]`;
const flattened = flattenObject(item, key);
Object.assign(result, flattened);
});
} else {
Object.entries(input).forEach(([key, value]) => {
const newKey = prefix === '' ? key : `${prefix}.${key}`;
const flattened = flattenObject(value, newKey);
Object.assign(result, flattened);
});
}
return result;
}
+6
-6
> @leather.io/utils@0.43.0 build /home/runner/work/mono/mono/packages/utils
> @leather.io/utils@0.44.0 build /home/runner/work/mono/mono/packages/utils
> tsup

@@ -11,7 +11,7 @@

ESM Build start
ESM dist/index.js 71.76 KB
ESM dist/index.js.map 135.06 KB
ESM ⚡️ Build success in 33ms
ESM dist/index.js 72.48 KB
ESM dist/index.js.map 137.40 KB
ESM ⚡️ Build success in 32ms
DTS Build start
DTS ⚡️ Build success in 1800ms
DTS dist/index.d.ts 13.38 KB
DTS ⚡️ Build success in 1841ms
DTS dist/index.d.ts 14.39 KB

@@ -335,2 +335,9 @@ # Changelog

## [0.44.0](https://github.com/leather-io/mono/compare/@leather.io/utils-v0.43.0...@leather.io/utils-v0.44.0) (2025-08-27)
### Features
* flatten object util ([cdebfec](https://github.com/leather-io/mono/commit/cdebfec46d68cb1ac9d12b844b9b4c7e976237d4))
## [0.43.0](https://github.com/leather-io/mono/compare/@leather.io/utils-v0.42.4...@leather.io/utils-v0.43.0) (2025-08-26)

@@ -337,0 +344,0 @@

@@ -173,2 +173,33 @@ import BigNumber, { BigNumber as BigNumber$1 } from 'bignumber.js';

type ObjectValue = string | number | boolean | null | NestedObject | ObjectValue[];
interface NestedObject {
[key: string]: ObjectValue;
}
interface FlattenedObject {
[key: string]: string | number | boolean | null;
}
/**
* Flattens a nested object or array into a flat object with dot-notation key paths.
*
* @param input - The object or array to flatten
* @param prefix - Internal parameter for recursion, represents the current key path
* @returns A flat object where keys represent the path to the original nested value
*
* @example
* ```typescript
* // Object example
* flattenObject({ layer1: { layer2: 'value' } })
* // Returns: { 'layer1.layer2': 'value' }
*
* // Array example
* flattenObject([{ key: 'value' }])
* // Returns: { '[0].key': 'value' }
*
* // Mixed example
* flattenObject({ users: [{ name: 'John', age: 30 }] })
* // Returns: { 'users[0].name': 'John', 'users[0].age': 30 }
* ```
*/
declare function flattenObject(input: ObjectValue, prefix?: string): FlattenedObject;
interface SpamFilterArgs {

@@ -253,2 +284,2 @@ input: string;

export { type CreateInscriptionData, type FormatAmountOptions, aggregateBaseCryptoAssetBalances, aggregateBtcBalances, aggregateStxBalances, assertExistence, assertIsTruthy, assertUnreachable, baseCurrencyAmountInQuote, baseCurrencyAmountInQuoteWithFallback, btcToSat, calculateMeanAverage, capitalize, convertAmountToBaseUnit, convertAmountToFractionalUnit, convertToMoneyTypeWithDefaultOfZero, countDecimals, createAccountAddresses, createBaseCryptoAssetBalance, createBtcBalance, createCounter, createCurrencyFormatter, createInscriptionAsset, createMoney, createMoneyFromDecimal, createNullArrayOfLength, createNumArrayOfRange, createStxBalance, dateToUnixTimestamp, daysInMs, daysInSec, defaultWalletKeyId, delay, ensureArray, extractPhraseFromString, fibonacciGenerator, fiveMinInMs, getAssetDisplayName, getAssetId, getTicker, hasBitcoinAddress, hasStacksAddress, hexToNumber, hoursInMs, hoursInSec, increaseValueByOneMicroStx, initBigNumber, invertExchangeRate, isBigInt, isBoolean, isDefined, isEmpty, isEmptyArray, isEmptyString, isError, isEven, isFiatCurrencyCode, isFulfilled, isFunction, isHexString, isMoney, isMoneyGreaterThanZero, isNumber, isNumberOrNumberList, isObject, isRejected, isString, isTypedArray, isUndefined, isValidPrecision, makeNumberRange, makeStacksTxExplorerLink, mapObject, match, matchesAssetId, maxMoney, microStxToStx, migratePositiveAssetBalancesToTop, minMoney, minutesInMs, minutesInSec, moneyToBaseUnit, noop, oneDayInMs, oneMinInMs, oneWeekInMs, propIfDefined, pxStringToNumber, quoteCurrencyAmountToBase, rebaseMarketData, removeTrailingNullCharacters, reverseBytes, safelyFormatHexTxid, sanitizeContent, satToBtc, scaleValue, secondsInMs, sortAssetsByName, spamFilter, stxToMicroStx, subtractMoney, sumMoney, sumNumbers, toHexString, truncateMiddle, undefinedIfLengthZero, uniqueArray, unitToFractionalUnit, weeksInMs, weeksInSec, whenInscriptionMimeType, whenNetwork };
export { type CreateInscriptionData, type FormatAmountOptions, aggregateBaseCryptoAssetBalances, aggregateBtcBalances, aggregateStxBalances, assertExistence, assertIsTruthy, assertUnreachable, baseCurrencyAmountInQuote, baseCurrencyAmountInQuoteWithFallback, btcToSat, calculateMeanAverage, capitalize, convertAmountToBaseUnit, convertAmountToFractionalUnit, convertToMoneyTypeWithDefaultOfZero, countDecimals, createAccountAddresses, createBaseCryptoAssetBalance, createBtcBalance, createCounter, createCurrencyFormatter, createInscriptionAsset, createMoney, createMoneyFromDecimal, createNullArrayOfLength, createNumArrayOfRange, createStxBalance, dateToUnixTimestamp, daysInMs, daysInSec, defaultWalletKeyId, delay, ensureArray, extractPhraseFromString, fibonacciGenerator, fiveMinInMs, flattenObject, getAssetDisplayName, getAssetId, getTicker, hasBitcoinAddress, hasStacksAddress, hexToNumber, hoursInMs, hoursInSec, increaseValueByOneMicroStx, initBigNumber, invertExchangeRate, isBigInt, isBoolean, isDefined, isEmpty, isEmptyArray, isEmptyString, isError, isEven, isFiatCurrencyCode, isFulfilled, isFunction, isHexString, isMoney, isMoneyGreaterThanZero, isNumber, isNumberOrNumberList, isObject, isRejected, isString, isTypedArray, isUndefined, isValidPrecision, makeNumberRange, makeStacksTxExplorerLink, mapObject, match, matchesAssetId, maxMoney, microStxToStx, migratePositiveAssetBalancesToTop, minMoney, minutesInMs, minutesInSec, moneyToBaseUnit, noop, oneDayInMs, oneMinInMs, oneWeekInMs, propIfDefined, pxStringToNumber, quoteCurrencyAmountToBase, rebaseMarketData, removeTrailingNullCharacters, reverseBytes, safelyFormatHexTxid, sanitizeContent, satToBtc, scaleValue, secondsInMs, sortAssetsByName, spamFilter, stxToMicroStx, subtractMoney, sumMoney, sumNumbers, toHexString, truncateMiddle, undefinedIfLengthZero, uniqueArray, unitToFractionalUnit, weeksInMs, weeksInSec, whenInscriptionMimeType, whenNetwork };

@@ -5,3 +5,3 @@ {

"description": "Shared bitcoin utilities",
"version": "0.43.0",
"version": "0.44.0",
"license": "MIT",

@@ -32,4 +32,4 @@ "homepage": "https://github.com/leather-io/mono/tree/dev/packages/utils",

"vitest": "2.1.9",
"@leather.io/tsconfig-config": "0.11.0",
"@leather.io/prettier-config": "0.8.1"
"@leather.io/prettier-config": "0.8.1",
"@leather.io/tsconfig-config": "0.11.0"
},

@@ -36,0 +36,0 @@ "keywords": [

@@ -19,2 +19,3 @@ import { BigNumber } from 'bignumber.js';

export * from './currency-formatter/currency-formatter';
export * from './flatten-object';

@@ -21,0 +22,0 @@ export { spamFilter } from './spam-filter/spam-filter';

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display