@httpx/stable-hash
Version:
Create keys or hashes from javascript values, useful for memoization or cache key generation.
1 lines • 12.4 kB
Source Map (JSON)
{"version":3,"sources":["../src/sort-arr.ts","../src/sort-obj-keys.ts","../src/create-stable-key-or-throw.ts","../src/hash-str.ts","../src/create-stable-hash-or-throw.ts","../src/create-stable-hash.ts","../src/create-stable-key.ts"],"names":["supportedTypes","collator","sortArr","arr","sortable","uniqueType","a","b","t1","sortObjKeys","object","sortedKeys","sorted","key","baseTypes","createStableKeyOrThrow","value","options","sortArrayValues","_","val","valType","isPlainObject","hashStr","message","algorithm","encoding","data","hashBuffer","hashArray","createStableHashOrThrow","createStableHash","createStableKey"],"mappings":"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","file":"index.mjs","sourcesContent":["const supportedTypes = new Set(['string', 'number', 'bigint']);\n\nconst collator = new Intl.Collator('en', {\n numeric: false,\n sensitivity: 'base',\n});\n\n/**\n * Will sort an array if its values are sortable and of the same type,\n * otherwise will return the original array untouched\n */\nexport const sortArr = <T>(arr: T[]): T[] => {\n if (arr.length <= 1) {\n return arr;\n }\n const sortable = [...arr];\n\n let uniqueType: null | string = null;\n\n // sort in ascending order\n try {\n sortable.sort((a, b) => {\n const t1 = typeof a;\n const t2 = typeof b;\n if (\n t1 !== t2 ||\n !supportedTypes.has(t1) ||\n (uniqueType !== null && uniqueType !== t1)\n ) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw 0;\n }\n uniqueType = t1 === 'bigint' ? 'number' : t1;\n if (t1 === 'string') {\n return collator.compare(a as string, b as string);\n } else if (t1 === 'number' || t1 === 'bigint') {\n return a < b ? -1 : a > b ? 1 : 0;\n }\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw 0;\n });\n } catch {\n // ignore sorting\n return arr;\n }\n return sortable;\n};\n","export const sortObjKeys = <T extends object>(object: T): T => {\n const sortedKeys = Object.keys(object).sort() as unknown as [keyof T];\n const sorted = {} as T;\n for (const key of sortedKeys) {\n sorted[key] = object[key];\n }\n return sorted;\n};\n","import { isPlainObject } from '@httpx/plain-object';\n\nimport { sortArr } from './sort-arr';\nimport { sortObjKeys } from './sort-obj-keys';\nimport type { CreateStableKeyOptions, SupportedDataTypesRW } from './types';\n\nconst baseTypes = new Set(['string', 'number', 'boolean']);\n\n/**\n * Create a stable key from a given value useful for caching or memoization.\n *\n * Object keys are sorted to maintain equality between objects with\n * the same keys but in different order.\n *\n * This function is\n * @example\n * ```typescript\n * import { createStableKeyOrThrow } from '@httpx/stable-hash';\n *\n * const params = {\n * key8: 'a string',\n * key1: 1,\n * key3: true,\n * key2: [3, 2, 1],\n * key7: {\n * key2: true,\n * key1: new Date('2025-02-11T08:58:32.075Z'),\n * },\n * };\n *\n * try {\n * const key = createStableKeyOrThrow(params);\n * // Will return a string containing\n * // \"{\"key1\":1,\"key2\":[1,2,3],\"key3\":true,\"key7\":{\"key1\":\"2025-02-11T08:58:32.075Z\",\"key2\":true},\"key8\":\"a string\"}\"\n * } catch (e) {\n * // TypeError in case of an unserializable data type\n * }\n * ```\n */\nexport const createStableKeyOrThrow = <T extends SupportedDataTypesRW>(\n value: T,\n options?: CreateStableKeyOptions\n): string => {\n const { sortArrayValues = true } = options ?? {};\n return JSON.stringify(value, (_, val) => {\n if (val === undefined) {\n return '[undefined]';\n }\n if (val === null) {\n return null;\n }\n const valType = typeof val;\n if (baseTypes.has(valType)) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return val;\n }\n if (valType === 'bigint') {\n return `[${val}n]`;\n }\n if (Array.isArray(val)) {\n return sortArrayValues ? sortArr<unknown>(val) : (val as unknown[]);\n }\n if (isPlainObject(val)) {\n return sortObjKeys(val);\n }\n if (val instanceof Date) {\n return val.toJSON();\n }\n throw new TypeError(`Unsupported data type: ${valType}`);\n });\n};\n","export type AsyncHashAlgorithms = 'SHA-256' | 'SHA-512';\n\nexport type HashStrOptions = {\n /**\n * Hashing algorithm to use\n */\n algorithm: AsyncHashAlgorithms;\n /**\n * Encode the hash in hexadecimal\n */\n encoding: 'hexa';\n};\n\nexport const hashStr = async (\n message: string,\n options: HashStrOptions\n): Promise<string> => {\n const { algorithm = 'SHA-265', encoding = 'hexa' } = options;\n const encoder = new TextEncoder();\n const data = encoder.encode(message);\n const hashBuffer = await globalThis.crypto.subtle.digest(algorithm, data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n if (encoding !== 'hexa') {\n throw new Error(`Unsupported encoding: ${encoding}`);\n }\n const hashHex = hashArray\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n return hashHex;\n};\n","import { createStableKeyOrThrow } from './create-stable-key-or-throw';\nimport { hashStr } from './hash-str';\nimport type { CreateStableHashOptions, SupportedDataTypesRW } from './types';\n\n/**\n * Create a stable hash (sha-256) from a given value useful for caching or memoization.\n *\n * @example\n * ```typescript\n * import { createStableHashOrThrow } from '@httpx/stable-hash';\n *\n * const params = {\n * key8: 'a string',\n * key1: 1,\n * key3: true,\n * key2: [3, 2, 1],\n * key7: {\n * key2: true,\n * key1: new Date('2025-02-11T08:58:32.075Z'),\n * },\n * };\n *\n * try {\n * const hash = await createStableHashOrThrow(params, {\n * // By default SHA-256 is used (SHA-512 available)\n * algorithm: 'SHA-256',\n * // By default the hash is encoded in hexadecimal\n * encoding: 'hexa',\n * });\n * // -> 'fb17a6300efcf62ae80708e2a672aee581b7f0dd7c6a9a7a748218846c679394'\n * } catch (e) {\n * // TypeError in case of an unserializable data type\n * }\n * ```\n */\nexport const createStableHashOrThrow = async <T extends SupportedDataTypesRW>(\n value: T,\n options?: CreateStableHashOptions\n): Promise<string> => {\n const { encoding = 'hexa', algorithm = 'SHA-256' } = options ?? {};\n return hashStr(createStableKeyOrThrow(value, options), {\n encoding,\n algorithm,\n });\n};\n","import { createStableHashOrThrow } from './create-stable-hash-or-throw';\nimport type { CreateStableHashOptions, SupportedDataTypesRW } from './types';\n\ntype Result =\n | { success: true; hash: string }\n | { success: false; error: Error };\n\n/**\n * Create a stable sha-256/hexadecimal hash from a value. Useful for caching\n * or memoization.\n *\n * Object keys are sorted to maintain equality between objects with\n * the same keys but in different order.\n *\n * @example\n * ```typescript\n * import { createStableHash } from '@httpx/stable-hash';\n *\n * const value = {\n * key8: 'a string',\n * key1: 1,\n * key3: true,\n * key2: [3, 2, 1],\n * key7: {\n * key2: true,\n * key1: new Date('2025-02-11T08:58:32.075Z'),\n * },\n * };\n *\n * const result = await createStableHash(value, {\n * // By default SHA-256 is used (SHA-512 available)\n * algorithm: 'SHA-256',\n * // By default the hash is encoded in hexadecimal\n * encoding: 'hexa',\n * });\n * if (!result.success) {\n * throw result.error;\n * }\n * const hash = result.hash;\n * // -> 'fb17a6300efcf62ae80708e2a672aee581b7f0dd7c6a9a7a748218846c679394'\n * ```\n */\nexport const createStableHash = async <T extends SupportedDataTypesRW>(\n value: T,\n options?: CreateStableHashOptions\n): Promise<Result> => {\n try {\n const hash = await createStableHashOrThrow(value, options);\n return {\n success: true,\n hash,\n };\n } catch (e) {\n return { success: false, error: e as TypeError };\n }\n};\n","import { createStableKeyOrThrow } from './create-stable-key-or-throw';\nimport type { CreateStableKeyOptions, SupportedDataTypesRW } from './types';\n\ntype Result = { success: true; key: string } | { success: false; error: Error };\n\n/**\n * Create a stable key from a given value useful for caching or memoization.\n *\n * Object keys are sorted to maintain equality between objects with\n * the same keys but in different order.\n *\n * This function is\n * @example\n * ```typescript\n * import { createStableKey } from '@httpx/stable-hash';\n *\n * const params = {\n * key8: 'a string',\n * key1: 1,\n * key3: true,\n * key2: [3, 2, 1],\n * key7: {\n * key2: true,\n * key1: new Date('2025-02-11T08:58:32.075Z'),\n * },\n * };\n *\n * const result = createStableKey(params);\n * if (!result.success) {\n * throw result.error;\n * }\n * const key = result.key;\n *\n * // Will return a string containing\n * // \"{\"key1\":1,\"key2\":[1,2,3],\"key3\":true,\"key7\":{\"key1\":\"2025-02-11T08:58:32.075Z\",\"key2\":true},\"key8\":\"a string\"}\"\n * ```\n */\nexport const createStableKey = <T extends SupportedDataTypesRW>(\n value: T,\n options?: CreateStableKeyOptions\n): Result => {\n try {\n const key = createStableKeyOrThrow(value, options);\n return {\n success: true,\n key,\n };\n } catch (e) {\n return { success: false, error: e as TypeError };\n }\n};\n"]}