{"id":15608695,"url":"https://github.com/hypercubed/samir","last_synced_at":"2026-02-13T10:16:26.679Z","repository":{"id":38319223,"uuid":"260776948","full_name":"Hypercubed/Samir","owner":"Hypercubed","description":"Samir uses an incremental implementation of the MurmurHash3 (32-bit) hashing algorithm and a plugable processing system to convert JS values and objects to a non-cryptographic hash.","archived":false,"fork":false,"pushed_at":"2023-05-28T10:58:18.000Z","size":155,"stargazers_count":1,"open_issues_count":22,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-11T19:44:22.428Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/Hypercubed.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"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}},"created_at":"2020-05-02T21:19:37.000Z","updated_at":"2022-06-06T20:42:03.000Z","dependencies_parsed_at":"2024-10-21T12:03:21.408Z","dependency_job_id":null,"html_url":"https://github.com/Hypercubed/Samir","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Hypercubed/Samir","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2FSamir","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2FSamir/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2FSamir/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2FSamir/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hypercubed","download_url":"https://codeload.github.com/Hypercubed/Samir/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hypercubed%2FSamir/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272641000,"owners_count":24968800,"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","status":"online","status_checked_at":"2025-08-29T02:00:10.610Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-10-03T05:21:55.498Z","updated_at":"2026-02-13T10:16:26.643Z","avatar_url":"https://github.com/Hypercubed.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Samir\n\n*OK, I'll do it.*\n\n![](https://i.imgflip.com/3ykd0c.jpg)\n\n## Goals\n\nSamir uses an incremental implementation of the MurmurHash3 (32-bit) hashing algorithm and a plugable processing system to convert JS values and objects to a non-cryptographic hash.  Co-worker of [Smykowski](https://github.com/Hypercubed/smykowski) and [Milton](https://github.com/Hypercubed/milton).\n\n## Features\n\n- Extensible\n- Incremental\n\n## Install\n\n```bash\nnpm i @hypercubed/samir\n```\n\n## Usage\n\n```js\nimport { Samir, defaultPlugins } from '@hypercubed/samir';\n\nconst hasher = new Samir();\nhasher.use(defaultPlugins);\n\nconst obj: any = {\n  null: null,\n  numbers: [\n    3.14159,\n    NaN,\n    Infinity,\n    -Infinity,\n    -0\n  ],\n  strings: {\n    empty: '',\n    string: 'foo',\n    multiline: `\n    This\n    is\n    multiline\n    `\n  },\n  arrays: {\n    empty: [],\n    array: [ 'one', 'two', 'three' ]\n  },\n  nested: { hello: 'hapi' },\n  false: false,\n  true: true,\n  undef: undefined,\n  regexp: /.*\\n/g,\n  function: function Yes() { /* noop */ },\n  map: new Map([['key1', 'value1'], ['key2', 'value2']]),\n  set: new Set([1, 2, 3]),\n  date: new Date('1972-02-12T03:24:00'),\n  objects: {\n    class: Samir,\n    instance: hasher\n  }\n};\n\nobj.self = obj;\nconsole.log(hasher.hash(obj));\n```\n\nprints:\n\n```\n1394490113\n```\n\nIncremtental hashing:\n\n```js\nhasher\n  .reset()                // resets the hash\n  .update('Hello');\n\n// Sometime later\n\nhasher\n  .update(' World!');\n\nconsole.log(hasher.result());  // Same as hasher.hash('Hello World')\n```\n\n## Description\n\n`Samir` is an interface for hashing JS objects and values.  In `Samir` we have a concept of plugins and presets. Plugins are functions that define a \"replacer\" functions.  Replacer functions accept each value and returns a stringified result (or the original value).  The value returned by the replacer function replaces the original value in the hashed result. Values returned from one replacer are passed down to the next untill the replacer returns a string.  Once a string is returned this string value is incrementally added to the hash.\n\nPlugins are added using the `.add` method on a `Samir` instance.  The order of the plugins does matter.  The first plugin to return a string wins.\n\nPresets are ordered sets of plugins.  You may use a preset by invoking the `.use` method on a `Samir` instance. \n\n```ascii\n| .............................. hash ................................ |\n        | .................... preset ................... |\n        | ... plugin ... |\n\n           +----------+     +----------+     +----------+\nInput  --\u003e | Replacer | --\u003e | Replacer | --\u003e | Replacer | --\u003e Output\n           +----------+     +----------+     +----------+\n\n```\n\nPresets and plugins may be used together:\n\n```ts\nconst hasher = new Samir();\nhasher\n  .use(defaultPlugins)\n  .add(myPlugin);\n```\n\n## Presets\n\n- `defaultPlugins` - includes all plugins below, in the order listed, with defualt settings.\n\n(see [presets.ts](https://github.com/Hypercubed/samir/blob/master/src/lib/presets.ts) for more)\n\n## Plugins\n\n- `primitives` - Generates hashes for primitive JS values, by default `number`, `boolean`, `bigint`, `undefined`, and `null`.\n- `symbols` - Generates hashes for symbols.  Global symbols generate repeatable hashes.  Ordinary symbols are unique and generate random hashes (repeatable within a JS environment)\n- `functions` - Genertates hases for functions based on toString.  Native functions use the native function name.\n- `dataTypes` - Handles JS object types like `Date` (based on getTime), `Error` (based on stack) and `RegExp` (based on toString).\n- `buffer` - Generates hashes for  `Buffer`s\n- `reference` - Captures and replaces repeated object references.  Improves hashing speed by not decending repeated itmes and allows cirecular objects and arrays.\n- `arrayDecender` - Recursively processes arrays\n- `objectDecender` - Recursively processes enumerable keys on objects, key order agnostic.\n- `setMap` - Recursively processes `Map`s and `Set`.  Map is key order agnostic.\n\n(see [plugins.ts](https://github.com/Hypercubed/samir/blob/master/src/lib/plugins.ts) for more)\n\n## Writing Plugins and Presets\n\nA plugin is a function that accepts an options object, the root value (the first value passed to the `samir#hash` or `samir#update` method), and a \"continue\" function used for recursion.  Each plugin must return a replacer/visitor function that is called (recursively) on each value to be hashed.\n\nFor example here is very simple plugin that will handle a hypothetical `Decimal` class:\n\n```ts\nconst decimalPlugin = () =\u003e (s: any) =\u003e {\n  if (s instanceof Decimal) {\n    return `$float#${s.toString()}`;\n  }\n  return s;\n};\n```\n\nIt is importrant that the replacer function return the input value if it is unaltered.\n\n(see [plugins.ts](https://github.com/Hypercubed/samir/blob/master/src/lib/plugins.ts) for more)\n\nPresets are functions that add plugins to a `samir` instance in a desiered order.  For example:\n\n```ts\nfunction mySamirPlugins(_: samir) {\n  _.add(decimalPlugin);\n  _.add(primitives);\n\n  _.add(reference);\n\n  _.add(arrayDecender);\n  _.add(objectDecender);\n\n  return _;\n}\n```\n\n## API\n\n### Class `Samir`\n\nCreates a new `Samir` instance with it's own Murmur3 incremental hasher\n\n`new Samir()`\n\n#### Method `samir.add(plugin[, options])`\n\nAdds a plugin to a `Samir` instance\n\n```ts\nadd(plugin: Plugin, options?: any): this\n```\n\n* `plugin` - A function that initializes and returns a replacer\n* `options` (optional, default = null) — Configuration for plugin\n\n#### Method `samir.use(preset)`\n\nAdds a preset (set of plugins) to a `Samir` instance\n\n```ts\nuse(preset: Preset, ...args?: any[]): this\n```\n\n* `preset` - A adds plugins to a samir instance in the desired order\n* `args` - Arguments taht are passed to the preset on initialization\n\n#### Method `samir.reset(seed)`\n\nResets the state object and using (optionally) the given seed (defaults to 0).\n\n```ts\nreset(seed?: number = 0): this\n```\n\n* `seed` (optional, default = 0)  - the hashing seed\n\n#### Method `samir.update(value)`\n\nPass the value through the `add`ed replacers, incrementally adding the resulting string to the hash.\n\n```ts\nupdate(value: any): this\n```\n\n* `value` - any value/object to hash as supported by the plugins\n\n#### Method `samir.result()`\n\nGet the result of the hash as a 32-bit positive integer.\n\n```ts\nresult(): number\n```\n\n#### Method `samir.hash(value, seed)`\n\nReset the hash, update with a value, return the result.  Calling `instance.hash(value, seed)` is the same as `instance.reset(seed).update(value).result()`.\n\n```ts\nhash(value: any, seed?: number = 0): number\n```\n\n* `value` - any value/object to hash as supported by the plugins\n* `seed` (optional, default = 0) - the hashing seed\n\n### `Replacer`\n\n```ts\ntype Replacer = (s: any, p: Path, value: any) =\u003e any ;\n```\n\n### `Plugin`\n\n```ts\ntype Plugin = (options: any, root: any, cont: StringifyFunction) =\u003e Replacer;\n```\n\n### `Preset`\n\n```ts\ntype Preset = (samir: samir) =\u003e samir;\n```\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhypercubed%2Fsamir","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhypercubed%2Fsamir","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhypercubed%2Fsamir/lists"}