Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
383c0a3939 | ||
|
|
f0849255df | ||
|
|
54aaa58990 | ||
|
|
59b03d5b51 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ztimson/utils",
|
"name": "@ztimson/utils",
|
||||||
"version": "0.30.9",
|
"version": "0.31.1",
|
||||||
"description": "Utility library",
|
"description": "Utility library",
|
||||||
"author": "Zak Timson",
|
"author": "Zak Timson",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ export function reservedIp(ip: string): boolean {
|
|||||||
* @returns {null | 'Invalid' | 'Host' | 'Loopback' | 'Private (Class A)' | 'Private (Class B)' | 'Link-Local' | 'IETF Protocol Assignments' | 'Documentation (TEST-NET-1)' | 'Benchmarking' | 'Documentation (TEST-NET-2)' | 'Documentation (TEST-NET-3)' | 'Multicast' | 'Reserved (Class E)'} null if public, class name if reserved
|
* @returns {null | 'Invalid' | 'Host' | 'Loopback' | 'Private (Class A)' | 'Private (Class B)' | 'Link-Local' | 'IETF Protocol Assignments' | 'Documentation (TEST-NET-1)' | 'Benchmarking' | 'Documentation (TEST-NET-2)' | 'Documentation (TEST-NET-3)' | 'Multicast' | 'Reserved (Class E)'} null if public, class name if reserved
|
||||||
*/
|
*/
|
||||||
export function isReserved(ip: string): null | 'Invalid' | 'Host' | 'Loopback' | 'Private (Class A)' | 'Private (Class B)' | 'Link-Local' | 'IETF Protocol Assignments' | 'Documentation (TEST-NET-1)' | 'Benchmarking' | 'Documentation (TEST-NET-2)' | 'Documentation (TEST-NET-3)' | 'Multicast' | 'Reserved (Class E)' {
|
export function isReserved(ip: string): null | 'Invalid' | 'Host' | 'Loopback' | 'Private (Class A)' | 'Private (Class B)' | 'Link-Local' | 'IETF Protocol Assignments' | 'Documentation (TEST-NET-1)' | 'Benchmarking' | 'Documentation (TEST-NET-2)' | 'Documentation (TEST-NET-3)' | 'Multicast' | 'Reserved (Class E)' {
|
||||||
const ipToNumber = (ip: string) => {
|
const ipToNumber = (ip: string) => ip.split('.').reduce((acc: number, octet: string) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
|
||||||
return ip.split('.').reduce((acc: number, octet: string) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ipNum = ipToNumber(ip);
|
const ipv4 = ip.includes(':') ? ipV6ToV4(ip) : ip;
|
||||||
if (ipNum === null) return 'Invalid';
|
if(ipv4 === null || !ipv4.includes('.')) return 'Invalid';
|
||||||
|
const ipNum = ipToNumber(ipv4);
|
||||||
|
if(ipNum === null) return 'Invalid';
|
||||||
|
|
||||||
const reserved = [
|
const reserved = [
|
||||||
{ name: "Host", start: ipToNumber("0.0.0.0"), end: ipToNumber("0.255.255.255") },
|
{ name: "Host", start: ipToNumber("0.0.0.0"), end: ipToNumber("0.255.255.255") },
|
||||||
|
|||||||
+32
-21
@@ -165,6 +165,15 @@ export function encodeQuery(data: any): string {
|
|||||||
).join('&');
|
).join('&');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Recursively walks obj/arrays, calling cb(dotPath, value) for every leaf+branch prop */
|
||||||
|
export function everyNested(obj: any, cb: (key: string, value: any) => void, prefix = ''){
|
||||||
|
Object.entries(obj).forEach(([k, v]) => {
|
||||||
|
const key = prefix ? `${prefix}.${k}` : k;
|
||||||
|
cb(key, v);
|
||||||
|
if (v && typeof v === 'object') everyNested(v, cb, key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively flatten a nested object, while maintaining key structure
|
* Recursively flatten a nested object, while maintaining key structure
|
||||||
*
|
*
|
||||||
@@ -234,50 +243,52 @@ export function includes(target: any, values: any, allowMissing = false): boolea
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deep check if two items are equal.
|
* Deep check if two items are equal.
|
||||||
* Handles primitives, objects, arrays, functions, Date, RegExp, and circular references.
|
* Handles primitives, objects, arrays, functions, Date, RegExp, circular refs,
|
||||||
|
* and optional key-based exclusions.
|
||||||
*
|
*
|
||||||
* @param {any} a - first item to compare
|
* @param {any} a - first item to compare
|
||||||
* @param {any} b - second item to compare
|
* @param {any} b - second item to compare
|
||||||
* @param {WeakMap<object, object>} [seen] - Internal parameter to track circular references
|
* @param {string[] | ((key: string, a: any, b: any) => boolean | void)} [compare] -
|
||||||
|
* Ignore patterns (dot-path, '*' = wildcard segment, matches whole subtree) OR
|
||||||
|
* a comparator callback: return true/false to short-circuit, undefined to use default logic.
|
||||||
|
* @param {WeakMap<object, object>} [seen] - Internal, tracks circular references
|
||||||
|
* @param {string} [path] - Internal, current dot-path
|
||||||
* @returns {boolean} True if they match
|
* @returns {boolean} True if they match
|
||||||
*/
|
*/
|
||||||
export function isEqual(a: any, b: any, seen = new WeakMap<object, object>()): boolean {
|
export function isEqual(a: any, b: any, compare?: string[] | ((key: string, a: any, b: any) => boolean | void)): boolean {
|
||||||
// Simple cases
|
const eq = (a: any, b: any, compare: any, seen: WeakMap<object, object>, path: string) => {
|
||||||
|
if(Array.isArray(compare)) {
|
||||||
|
if(compare.some(p => new RegExp(`^${p.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '[^.]*')}(\\.|$)`).test(path))) return true;
|
||||||
|
} else if(typeof compare === 'function') {
|
||||||
|
const r = compare(path, a, b);
|
||||||
|
if(r !== undefined) return r;
|
||||||
|
}
|
||||||
if(a === b) return true;
|
if(a === b) return true;
|
||||||
if(a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
if(a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
|
||||||
if(a instanceof RegExp && b instanceof RegExp) return a.source === b.source && a.flags === b.flags;
|
if(a instanceof RegExp && b instanceof RegExp) return a.source === b.source && a.flags === b.flags;
|
||||||
|
|
||||||
// Null checks
|
|
||||||
if(typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
|
if(typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
|
||||||
if(Number.isNaN(a) && Number.isNaN(b)) return true;
|
if(Number.isNaN(a) && Number.isNaN(b)) return true;
|
||||||
if(typeof a === 'function' && typeof b === 'function') return a.toString() === b.toString()
|
if(typeof a === 'function' && typeof b === 'function') return a.toString() === b.toString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Circular references
|
|
||||||
if(seen.has(a)) return seen.get(a) === b;
|
if(seen.has(a)) return seen.get(a) === b;
|
||||||
seen.set(a, b);
|
seen.set(a, b);
|
||||||
const isArrayA = Array.isArray(a);
|
const isArrayA = Array.isArray(a), isArrayB = Array.isArray(b);
|
||||||
const isArrayB = Array.isArray(b);
|
|
||||||
|
|
||||||
// Array checks
|
|
||||||
if(isArrayA && isArrayB) {
|
if(isArrayA && isArrayB) {
|
||||||
if(a.length !== b.length) return false;
|
if(a.length !== b.length) return false;
|
||||||
for(let i = 0; i < a.length; i++) {
|
for(let i = 0; i < a.length; i++) if(!eq(a[i], b[i], compare, seen, path ? `${path}.${i}` : `${i}`)) return false;
|
||||||
if(!isEqual(a[i], b[i], seen)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(isArrayA !== isArrayB) return false;
|
if(isArrayA !== isArrayB) return false;
|
||||||
|
const keysA = Object.keys(a), keysB = Object.keys(b);
|
||||||
// Key & value deep comparison
|
|
||||||
const keysA = Object.keys(a);
|
|
||||||
const keysB = Object.keys(b);
|
|
||||||
if(keysA.length !== keysB.length) return false;
|
if(keysA.length !== keysB.length) return false;
|
||||||
for(const key of keysA) {
|
for(const key of keysA) {
|
||||||
if(!Object.prototype.hasOwnProperty.call(b, key) || !isEqual(a[key], b[key], seen)) return false;
|
if(!Object.prototype.hasOwnProperty.call(b, key) || !eq(a[key], b[key], compare, seen, path ? `${path}.${key}` : key)) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return eq(a, b, compare, new WeakMap(), '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user