{"id":13725644,"url":"https://github.com/licitdev/seeded-hashids","last_synced_at":"2025-03-21T00:31:51.769Z","repository":{"id":34997749,"uuid":"195078536","full_name":"licitdev/seeded-hashids","owner":"licitdev","description":"Generate seeded Hashids that is unique per scope.","archived":false,"fork":false,"pushed_at":"2025-02-20T11:44:04.000Z","size":679,"stargazers_count":10,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-17T19:21:23.763Z","etag":null,"topics":["decode","encode","hacktoberfest","hash","hashids","javascript","node","nodejs","objectid","scope","seeded"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/licitdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["licitdev"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2019-07-03T15:10:41.000Z","updated_at":"2025-02-20T11:44:01.000Z","dependencies_parsed_at":"2024-10-28T09:12:07.462Z","dependency_job_id":"f3da7533-5ee9-493f-b89d-8045306100dd","html_url":"https://github.com/licitdev/seeded-hashids","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/licitdev%2Fseeded-hashids","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/licitdev%2Fseeded-hashids/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/licitdev%2Fseeded-hashids/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/licitdev%2Fseeded-hashids/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/licitdev","download_url":"https://codeload.github.com/licitdev/seeded-hashids/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244717391,"owners_count":20498283,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["decode","encode","hacktoberfest","hash","hashids","javascript","node","nodejs","objectid","scope","seeded"],"created_at":"2024-08-03T01:02:30.049Z","updated_at":"2025-03-21T00:31:51.422Z","avatar_url":"https://github.com/licitdev.png","language":"JavaScript","readme":"# Seeded-Hashids\nGenerate seeded Hashids that is unique per seed.\n\n[![NPM version][npm-version-image]][npm-url]\n[![License][license-image]][license-url]\n[![Build Status][travis-image]][travis-url]\n[![Coveralls Status][coveralls-image]][coveralls-url]\n[![NPM downloads][npm-downloads-image]][npm-url]\n\n**Seeded-Hashids** is an easy to use library to generate seeded [Hashids](http://hashids.org/javascript) which is unique to a seed that can be based on a user or group. Hide the raw ids, hex strings, objectids or uuids from end users and reduces the number of database calls that check for valid or existing ids.\n\nWorks well with databases that has numeric keys or hex strings. Your database will contain only the original ids as there is no need to store the encoded versions. UUIDs and MongoDB's ObjectIDs are hex strings.\n\nAn example is to generate Hashids that are unique to a particular application. Even if multiple applications shared their userids with each other, the users could not be correlated or identified by their userids.\n\n## Sample Scenario\n\n```\nEncoding the userids and using their actual userid as a seed.\nThe userids are unique and never revealed to end users.\nUser A (ID 123)\nUser B (ID 456)\nUser C (ID 789)\n\nApplication A sees User B as 'zxcqwe' and sees User C as 'bnmrty'\nApplication B sees User A as 'qweasd' and sees User C as 'rtyjkl'\nApplication C sees User A as 'fghzxc' and sees User B as 'asdiop'\n\n'asdiop' is supposedly only visible to Application C.\nIf Application A decodes 'asdiop', it decodes to an empty string.\n```\n\n## Getting started\n\nInstall Seeded-Hashids via:\n\n- [npm](https://www.npmjs.com/package/seeded-hashids): `npm install --save seeded-hashids`\n- [yarn](https://yarnpkg.com/package/seeded-hashids): `yarn add seeded-hashids`\n\nSample code:\n\n```javascript\nconst seededHashids = require('seeded-hashids');\nconst ObjectId = require('mongoose').Types.ObjectId;\nconst scopes = [\n  {scope: 'user', salt: 'some-salt'}\n];\n\nseededHashids.initialize({scopes: scopes, objectId: ObjectId});\n\nlet encoded, decoded;\n\n// Encoding hex strings\nencoded = seededHashids.encodeHex('user', 'abcd1234');\ndecoded = seededHashids.decodeHex('user', encoded);\nconsole.log(encoded); // 'bNVA3Q9g'\nconsole.log(decoded); // 'abcd1234'\n\n// Encoding hex strings with seed\nencoded = seededHashids.encodeHex('user', 'abcd1234', 'unique-seed');\ndecoded = seededHashids.decodeHex('user', encoded, 'unique-seed');\nconsole.log(encoded); // 'S4RTRZ2L'\nconsole.log(decoded); // 'abcd1234'\n\n// If a wrong seed is used for decodeHex, will decode to a different output\ndecoded = seededHashids.decodeHex('user', encoded, 'wrong-seed');\nconsole.log(decoded); // '' (Empty string)\n\n// Decoding ObjectIds, same as hex but needs to be 24 characters hex string\nencoded = seededHashids.encodeHex('user', 'abcd1234abcd1234abcd1234');\ndecoded = seededHashids.decodeObjectId('user', encoded);\nconsole.log(encoded); // '4Wg453PYPrdhAEdyeMYWpm'\nconsole.log(decoded); // ObjectId('abcd1234abcd1234abcd1234')\n\n// Decoding ObjectIds with seed, same as hex but needs to be 24 characters hex string\nencoded = seededHashids.encodeHex('user', 'abcd1234abcd1234abcd1234', 'unique-seed');\ndecoded = seededHashids.decodeObjectId('user', encoded, 'unique-seed');\nconsole.log(encoded); // 'QX3Bu2pNSTnPEZFg6sW5EY'\nconsole.log(decoded); // ObjectId('abcd1234abcd1234abcd1234')\n\n// Encoding positive integers\nencoded = seededHashids.encode('user', 12345678);\ndecoded = seededHashids.decode('user', encoded);\nconsole.log(encoded); // 'nY9AyaDn'\nconsole.log(decoded); // 12345678\n\n// Encoding positive integers with seed\nencoded = seededHashids.encode('user', 12345678, 'unique-seed');\ndecoded = seededHashids.decode('user', encoded, 'unique-seed');\nconsole.log(encoded); // 'aNq4PsAx'\nconsole.log(decoded); // 12345678\n\n// If a wrong seed is used for decode, will decode to a different output\ndecoded = seededHashids.decode('user', encoded, 'wrong-seed');\nconsole.log(decoded); // NaN (Different output)\n\n// Encoding array of positive integers\nencoded = seededHashids.encode('user', [1,2,3,4,5,6,7,8]);\ndecoded = seededHashids.decode('user', encoded);\nconsole.log(encoded); // '6ZsyFeUKc5fahqS5'\nconsole.log(decoded); // [1,2,3,4,5,6,7,8]\n\n// Encoding array of positive integers with seed\nencoded = seededHashids.encode('user', [1,2,3,4,5,6,7,8], 'unique-seed');\ndecoded = seededHashids.decode('user', encoded, 'unique-seed');\nconsole.log(encoded); // '9bzhGs3kHZVJs7wm'\nconsole.log(decoded); // [1,2,3,4,5,6,7,8]\n```\n\n## API\n\n### **initialize (options)** : noResult `undefined`\n\n\u003e To set up the required scopes and other parameters.\n\n```javascript\nseededHashids.initialize({\n  scopes: scopes,\n  charset: charset,\n  minOutputLength: minOutputLength,\n  shuffleOutput: shuffleOutput,\n  objectId: objectId,\n  shuffleFunction: shuffleFunction,\n  unshuffleFunction: unshuffleFunction\n});\n```\n\n#### options `Object`\n\nField | Required | Type | Defaults\n:---  | :---: | :---: | :---\nscopes | yes | `Array` | -\ncharset | no | `String` | `a-z, A-Z, 2-9` without `i, I, o, O, 1, 0` to increase readibility\nminOutputLength | no | `Number` |  8\nshuffleOutput | no | `Boolean` | true\nobjectId | no | `ObjectId` | -\nshuffleFunction | no | `Function` | Built-in shuffle function\nunshuffleFunction | no | `Function` | Built-in unshuffle function\n\n##### scopes `Array`\n\n- The array is a list of scope object that contains a scope string and a salt string.\n- Each scope could be then name of a class or an object type.\n- Scopes have to be unique.\n- Salts have to be unique.\n\n```javascript\nlet scope = [\n  {scope: 'user', salt: 'some-salt'},\n  {scope: 'profile', salt: 'another-salt'}\n];\n```\n\n##### charset `String` *(optional)*\n\n- This value is passed directly to Hashids.\n- A minimum of 16 unique characters are required.\n\n```javascript\nlet charset = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';\n```\n\n##### minOutputLength `Number` *(optional)*\n\n- This value is passed directly to Hashids, which adds padding to reach the length required.\n\n```javascript\nlet minOutputLength = 8;\n```\n\n##### shuffleOutput `Boolean` *(optional)*\n\n- This value determines if the output seeded-hashid will be shuffled after encoding and before decoding by Hashids.\n- The output is shuffled based on the seed and attempts to prevent decoding using a wrong seed.\n- If no seed is provided, the output seeded-hashid will not be shuffled.\n\n```javascript\nlet shuffleOutput = true;\n```\n\n##### objectId `Function` *(optional)*\n\n- This object is required only if there is a need to cast the decoding output to an ObjectId using `.decodeObjectId`.\n- Can pass in `require('mongoose').Types.ObjectId ` or `require('mongodb').ObjectId` or functions.\n\n```javascript\nlet objectId = require('mongoose').Types.ObjectId;\n```\n\n##### shuffleFunction `Function` *(optional)*\n\n- Change the shuffle function used.\n- The shuffle function needs to accept (inputString, seedString) and returns an outputString.\n\n```javascript\nlet shuffleFunction = function(inputString, seedString){\n  return require('shuffle-seed').shuffle(inputString.split(''), seedString).join('');\n};\n```\n\n##### unshuffleFunction `Function` *(optional)*\n\n- Change the unshuffle function used.\n- The unshuffle function needs to accept (inputString, seedString) and returns an outputString.\n\n```javascript\nlet unshuffleFunction = function(inputString, seedString){\n  return require('shuffle-seed').unshuffle(inputString.split(''), seedString).join('');\n};\n```\n\n---\n### **encode (scope, data, [seed])** : seededHashid `String`\n\n\u003e To encode positive numbers.\n\n```javascript\nlet userId = seededHashids.encode('user', 12345678);\n```\n\n#### scope `String`\n\n- This scope should be the same scope string that was used during initialization.\n\n#### data `Number` or `Array of Numbers`\n\n- The positive number or the array of positive numbers to be encoded.\n\n#### seed `String` *(optional)*\n\n- This seed is used to encode a seeded-hashid that is unique to the seed.\n\n---\n### **encodeHex (scope, hex, [seed])** : seededHashid `String`\n\n\u003e To encode hex strings.\n\n```javascript\nlet userId = seededHashids.encodeHex('user', 'abcd1234', 'unique-seed');\n```\n\n#### scope `String`\n\n- This scope should be the same scope string that was used during initialization.\n\n#### hex `String`\n\n- This hex string to be encoded.\n\n#### seed `String` *(optional)*\n\n- This seed is used to encode a seeded-hashid that is unique to the seed.\n\n---\n### **decode (scope, seededHashid, [seed])** : decodedData `Number` or `Array of Numbers`\n\n\u003e To decode seeded-hashid into a positive number or an array of positive numbers. Returns NaN if unable to decode.\n\n```javascript\nlet userId = seededHashids.decode('user', 'nY9AyaDn', 'unique-seed');\n```\n\n#### scope `String`\n\n- This scope should be the same scope string that was used during initialization.\n\n#### seededHashid `String`\n\n- This seeded-hashid to be decoded.\n\n#### seed `String` *(optional)*\n\n- This seed is used to decode a seeded-hashid that is unique to the seed.\n\n---\n### **decodeHex (scope, seededHashid, [seed])** : decodedHex `String`\n\n\u003e To decode seeded-hashid into a hex string. Returns an empty string if unable to decode.\n\n```javascript\nlet userId = seededHashids.decodeHex('user', 'S4RTRZ2L', 'unique-seed');\n```\n\n#### scope `String`\n\n- This scope should be the same scope string that was used during initialization.\n\n#### seededHashid `String`\n\n- This seeded-hashid to be decoded.\n\n#### seed `String` *(optional)*\n\n- This seed is used to decode a seeded-hashid that is unique to the seed.\n\n---\n### **decodeObjectId (scope, seededHashid, [seed])** : decodedObjectId `ObjectId`\n\n\u003e To decode seeded-hashid into an objectId. Returns NaN if unable to decode.\n\n```javascript\nlet userId = seededHashids.decodeObjectId('user', 'QX3Bu2pNSTnPEZFg6sW5EY', 'unique-seed');\n```\n\n#### scope `String`\n\n- This scope should be the same scope string that was used during initialization.\n\n#### seededHashid `String`\n\n- This seeded-hashid to be decoded.\n\n#### seed `String` *(optional)*\n\n- This seed is used to decode a seeded-hashid that is unique to the seed.\n\n---\n### **reset ()** : noResult `undefined`\n\n\u003e To reset seededHashids, needs to initialize() again before usage.\n\n```javascript\nseededHashids.reset();\n```\n\n---\n### **isInitialized ()** : isInitialized `Boolean`\n\n\u003e To check if seededHashids is initialized.\n\n```javascript\nlet isInitialized = seededHashids.isInitialized();\n```\n\n---\n### **getScopes ()** : scopes `Array`\n\n\u003e To get the string array of scopes.\n\n```javascript\nlet scopes = seededHashids.getScopes();\n```\n\n---\n### **getCharset ()** : charset `String`\n\n\u003e To get the charset string.\n\n```javascript\nlet charset = seededHashids.getCharset();\n```\n\n---\n### **getMinOutputLength ()** : minOutputLength `Number`\n\n\u003e To get the minimum output seeded-hashid length.\n\n```javascript\nlet minOutputLength = seededHashids.getMinOutputLength();\n```\n\n---\n### **getShuffleOutput ()** : shuffleOutput `Boolean`\n\n\u003e To check if the output will be shuffled if a seed is provided.\n\n```javascript\nlet shuffleOutput = seededHashids.getShuffleOutput();\n```\n\n---\n### **getObjectId ()** : objectId `Function`\n\n\u003e To get the objectId function to see if available.\n\n```javascript\nlet objectId = seededHashids.getObjectId();\n```\n\n---\n### **getShuffleFunction ()** : shuffleFunction `Function`\n\n\u003e To get the shuffle function used.\n\n```javascript\nlet shuffleFunction = seededHashids.getShuffleFunction();\n```\n\n---\n### **getUnshuffleFunction ()** : unshuffleFunction `Function`\n\n\u003e To get the unshuffle function used.\n\n```javascript\nlet unshuffleFunction = seededHashids.getUnshuffleFunction();\n```\n\n## Recommendations\n\n1. Charset should **not** be too short.\n2. Salts should **not** be too short.\n3. Seeds should **not** be too short. Recommended to use **long** hex strings such as ObjectIds or UUIDs.\n4. Encode **longer** input hex strings such as ObjectIds or UUIDs.\n5. Always **validate** the output after decoding.\n6. The minOutputLength should **not** be too small.\n7. Leave the shuffleOutput as **true**, which is the default value.\n8. Encode and decode as required, recommended for database to contain only **original** ids or hex strings.\n\n## Pitfalls\n\n1. Encoding of an array of numbers is supported but the numbers within are **not** individually shuffled.\n2. Encoding of negative numbers are **not** supported.\n3. Required to pass in the **correct type** of parameters in order to prevent the encoding of invalid seeded-hashids by accident, such as encoding `\"[object Object]\"`.\n4. It could still be **possible** for a different seed to decode a seeded-hashid, but it is really rare if the **recommendations** are followed.\n5. Upgrade to a major version **after testing** as the output seeded-hashids may have changed.\n6. Do **not** use this library as a security tool and do **not** encode sensitive data. This is **not** an encryption library.\n\n## License\n\nMIT License. See the [LICENSE](LICENSE) file.\n\n[travis-url]: https://travis-ci.com/licitdev/seeded-hashids\n[travis-image]: https://img.shields.io/travis/com/licitdev/seeded-hashids.svg?style=flat-square\n\n[coveralls-url]: https://coveralls.io/github/licitdev/seeded-hashids\n[coveralls-image]: https://img.shields.io/coveralls/github/licitdev/seeded-hashids.svg?style=flat-square\n\n[npm-downloads-image]: https://img.shields.io/npm/dm/seeded-hashids.svg?style=flat-square\n[npm-version-image]: https://img.shields.io/npm/v/seeded-hashids.svg?style=flat-square\n[npm-url]: https://www.npmjs.com/package/seeded-hashids\n\n[license-url]: https://github.com/licitdev/seeded-hashids/blob/master/LICENSE\n[license-image]: https://img.shields.io/npm/l/seeded-hashids.svg?style=flat-square","funding_links":["https://github.com/sponsors/licitdev"],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flicitdev%2Fseeded-hashids","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flicitdev%2Fseeded-hashids","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flicitdev%2Fseeded-hashids/lists"}