mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-09-10 22:14:48 -05:00
feat(json-hash): extract into own package
This commit is contained in:
16
@vates/json-hash/.USAGE.md
Normal file
16
@vates/json-hash/.USAGE.md
Normal file
@@ -0,0 +1,16 @@
|
||||
The use for this librariy is to create a composite key from a JSON value, ignoring objects identity and properties order.
|
||||
|
||||
The hash algorithm used is intended to be fast and with low collisions and is not guaranteed to be be secure.
|
||||
|
||||
```js
|
||||
import { jsonHash } from '@vates/json-hash'
|
||||
|
||||
console.log(jsonHash('foo'))
|
||||
// → "siEyldVkkW+JpqQkVVZ8h8P0gPzXocFeIg8X1xaaeQs="
|
||||
|
||||
// order of properties is ignored
|
||||
console.log(jsonHash({ foo: 0, bar: 1 }))
|
||||
// → "JckoRSMIBjNlgEWIXhgpBOuyLQYqABZqvf1ccb3BPg0="
|
||||
console.log(jsonHash({ bar: 1, foo: 0 }))
|
||||
// → "JckoRSMIBjNlgEWIXhgpBOuyLQYqABZqvf1ccb3BPg0="
|
||||
```
|
||||
1
@vates/json-hash/.npmignore
Symbolic link
1
@vates/json-hash/.npmignore
Symbolic link
@@ -0,0 +1 @@
|
||||
../../scripts/npmignore
|
||||
49
@vates/json-hash/README.md
Normal file
49
@vates/json-hash/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->
|
||||
|
||||
# @vates/json-hash
|
||||
|
||||
[](https://npmjs.org/package/@vates/json-hash)  [](https://bundlephobia.com/result?p=@vates/json-hash) [](https://npmjs.org/package/@vates/json-hash)
|
||||
|
||||
> Compute a stable hash from a JSON-ifiable value
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/@vates/json-hash):
|
||||
|
||||
```sh
|
||||
npm install --save @vates/json-hash
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The use for this librariy is to create a composite key from a JSON value, ignoring objects identity and properties order.
|
||||
|
||||
The hash algorithm used is intended to be fast and with low collisions and is not guaranteed to be be secure.
|
||||
|
||||
```js
|
||||
import { jsonHash } from '@vates/json-hash'
|
||||
|
||||
console.log(jsonHash('foo'))
|
||||
// → "siEyldVkkW+JpqQkVVZ8h8P0gPzXocFeIg8X1xaaeQs="
|
||||
|
||||
// order of properties is ignored
|
||||
console.log(jsonHash({ foo: 0, bar: 1 }))
|
||||
// → "JckoRSMIBjNlgEWIXhgpBOuyLQYqABZqvf1ccb3BPg0="
|
||||
console.log(jsonHash({ bar: 1, foo: 0 }))
|
||||
// → "JckoRSMIBjNlgEWIXhgpBOuyLQYqABZqvf1ccb3BPg0="
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are _very_ welcomed, either on the documentation or on
|
||||
the code.
|
||||
|
||||
You may:
|
||||
|
||||
- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)
|
||||
38
@vates/json-hash/index.js
Normal file
38
@vates/json-hash/index.js
Normal file
@@ -0,0 +1,38 @@
|
||||
'use strict'
|
||||
|
||||
const { createHash } = require('node:crypto')
|
||||
|
||||
function updateJsonHash(value, hash) {
|
||||
if (value !== null && typeof value === 'object') {
|
||||
if (Array.isArray(value)) {
|
||||
hash.update('[')
|
||||
for (const item of value) {
|
||||
updateJsonHash(item, hash)
|
||||
|
||||
// trailing is not a big deal because it does not need to be valid JSON
|
||||
hash.update(',')
|
||||
}
|
||||
hash.update(']')
|
||||
} else {
|
||||
hash.update('{')
|
||||
for (const key of Object.keys(value).sort()) {
|
||||
updateJsonHash(key, hash)
|
||||
hash.update(':')
|
||||
updateJsonHash(value[key], hash)
|
||||
|
||||
// trailing is not a big deal because it does not need to be valid JSON
|
||||
hash.update(',')
|
||||
}
|
||||
hash.update('}')
|
||||
}
|
||||
} else {
|
||||
hash.update(JSON.stringify(value))
|
||||
}
|
||||
}
|
||||
|
||||
exports.jsonHash = function jsonHash(value) {
|
||||
// this hash does not need to be secure, it just needs to be fast and with low collisions
|
||||
const hash = createHash('sha256')
|
||||
updateJsonHash(value, hash)
|
||||
return hash.digest('base64')
|
||||
}
|
||||
19
@vates/json-hash/index.test.js
Normal file
19
@vates/json-hash/index.test.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const { jsonHash } = require('@vates/json-hash')
|
||||
const assert = require('node:assert/strict')
|
||||
const test = require('test')
|
||||
|
||||
const cases = [
|
||||
[null, 'dCNOmK/nSY+12vHzasLXiswzlGT5UHA7jAGYkvmCuQs='],
|
||||
[42, 'c0dctApWjo2ooEXO0RATfhWfiQrE2og7axfcZRs6gEk='],
|
||||
['foo', 'siEyldVkkW+JpqQkVVZ8h8P0gPzXocFeIg8X1xaaeQs='],
|
||||
[[], 'T1PNoYwrqgwDVLtfmj7L5e0Sq02OEbqHPC8RFhICuUU='],
|
||||
[{}, 'RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o='],
|
||||
[{ a: 1, b: 2 }, 'LMjVxyj2vtMZ3W1cZ2cJrQij5zn7FBOlPiyBB38OmFE='],
|
||||
[{ b: 2, a: 1 }, 'LMjVxyj2vtMZ3W1cZ2cJrQij5zn7FBOlPiyBB38OmFE='],
|
||||
]
|
||||
|
||||
for (const [value, hash] of cases) {
|
||||
test(JSON.stringify(value), function () {
|
||||
assert.equal(jsonHash(value), hash)
|
||||
})
|
||||
}
|
||||
33
@vates/json-hash/package.json
Normal file
33
@vates/json-hash/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "@vates/json-hash",
|
||||
"description": "Compute a stable hash from a JSON-ifiable value",
|
||||
"keywords": [
|
||||
"hash",
|
||||
"json",
|
||||
"stable"
|
||||
],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@vates/json-hash",
|
||||
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||
"repository": {
|
||||
"directory": "@vates/json-hash",
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Vates SAS",
|
||||
"url": "https://vates.fr"
|
||||
},
|
||||
"license": "ISC",
|
||||
"version": "0.0.0",
|
||||
"engines": {
|
||||
"node": ">=8.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"test": "^3.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postversion": "npm publish --access public",
|
||||
"test": "node--test"
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,10 @@ import { Agent, ProxyAgent, request } from 'undici'
|
||||
import { coalesceCalls } from '@vates/coalesce-calls'
|
||||
import { Collection } from 'xo-collection'
|
||||
import { compose } from '@vates/compose'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
import { EventEmitter } from 'events'
|
||||
import { Index } from 'xo-collection/index.js'
|
||||
import { jsonHash } from '@vates/json-hash'
|
||||
import { cancelable, defer, fromCallback, ignoreErrors, pDelay, pRetry, pTimeout } from 'promise-toolbox'
|
||||
import { limitConcurrency } from 'limit-concurrency-decorator'
|
||||
import { decorateClass } from '@vates/decorate-with'
|
||||
@@ -90,41 +90,6 @@ const addSyncStackTrace = async promise => {
|
||||
}
|
||||
}
|
||||
|
||||
function updateJsonHash(value, hash) {
|
||||
if (value !== null && typeof value === 'object') {
|
||||
if (Array.isArray(value)) {
|
||||
hash.update('[')
|
||||
for (const item of value) {
|
||||
updateJsonHash(item, hash)
|
||||
|
||||
// trailing is not a big deal because it does not need to be valid JSON
|
||||
hash.update(',')
|
||||
}
|
||||
hash.update(']')
|
||||
} else {
|
||||
hash.update('{')
|
||||
for (const key of Object.keys(value).sort()) {
|
||||
updateJsonHash(key, hash)
|
||||
hash.update(':')
|
||||
updateJsonHash(value[key], hash)
|
||||
|
||||
// trailing is not a big deal because it does not need to be valid JSON
|
||||
hash.update(',')
|
||||
}
|
||||
hash.update('}')
|
||||
}
|
||||
} else {
|
||||
hash.update(JSON.stringify(value))
|
||||
}
|
||||
}
|
||||
|
||||
export function jsonHash(value) {
|
||||
// this hash does not need to be secure, it just needs to be fast and with low collisions
|
||||
const hash = createHash('sha256')
|
||||
updateJsonHash(value, hash)
|
||||
return hash.digest('base64')
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export class Xapi extends EventEmitter {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"@vates/coalesce-calls": "^0.1.0",
|
||||
"@vates/compose": "^2.1.0",
|
||||
"@vates/decorate-with": "^2.1.0",
|
||||
"@vates/json-hash": "^0.0.0",
|
||||
"@vates/obfuscate": "^0.1.0",
|
||||
"@vates/xml": "^2.0.0",
|
||||
"@vates/xml-rpc": "^1.0.0",
|
||||
|
||||
Reference in New Issue
Block a user