UNPKG

@httpx/stable-hash

Version:

Create keys or hashes from javascript values, useful for memoization or cache key generation.

1 lines 12.4 kB
{"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.cjs","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"]}