adding suport for CRC32 SFV
This commit is contained in:
@@ -1 +1,75 @@
|
|||||||
|
# Meta-Hash
|
||||||
|
|
||||||
|
Meta-Hash is a TypeScript library designed to monitor a specified folder and compute hashes for any new files detected within it. This library leverages an index system to avoid recomputing hashes if the program is restarted, ensuring efficiency and continuity in hash computation. The hashes are computed in the Content Identifier (CID) format, making them compatible with systems like IPFS (InterPlanetary File System).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Efficient Hash Computation**: Automatically computes hashes for new files using multiple algorithms, including SHA2-256, SHA1, MD5, SHA3-256 and more.
|
||||||
|
- **Persistent Indexing**: Utilizes an index system to record file hashes, sizes, and modification times, preventing the need for rehashing on program restarts.
|
||||||
|
- **CID Format**: Hashes are stored in the CID format, facilitating integration with IPFS and similar distributed file systems.
|
||||||
|
|
||||||
|
## CID Format and Inspection
|
||||||
|
|
||||||
|
The Content Identifier (CID) is a label used to point to material in IPFS. It doesn't indicate where the content is stored, but it forms a kind of address based on the content itself. CIDs are used as a standard way of pointing to pieces of information. A single CID can represent a piece of content that is distributed across multiple systems.
|
||||||
|
|
||||||
|
The list of CID formats used in this project can be found at the following link:
|
||||||
|
[Multicodecs Table](https://ipfs.io/ipfs/QmXec1jjwzxWJoNbxQF5KffL8q6hFXm9QwUGaa3wKGk6dT/#title=Multicodecs&src=https://raw.githubusercontent.com/multiformats/multicodec/master/table.csv)
|
||||||
|
|
||||||
|
To inspect the details of a CID, including its version, codec, multihash and more, you can use the CID Inspector tool. This tool allows you to enter a CID and it will break it down into its component parts and provide a detailed explanation of each part.
|
||||||
|
|
||||||
|
[CID Inspector Tool](https://cid.ipfs.tech/)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To use Meta-Hash in your project, clone this repository and install the required dependencies.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#to install
|
||||||
|
yarn add metazla@[email protected]:Metazla/meta-hash.git
|
||||||
|
|
||||||
|
#to update
|
||||||
|
yarn up -i @metazla/meta-hash
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage of ComputeHashIndexCache
|
||||||
|
|
||||||
|
To integrate Meta-Hash into your application, follow these steps:
|
||||||
|
|
||||||
|
1. **Import the Library**: Import the necessary components from the library.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ComputeHashIndexCache, CID_ALGORITHM_NAMES } from "./folder-watch-hasher";
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Initialize the Watcher**: Create an instance of `ComputeHashIndexCache`, specifying the path to the folder you want to monitor and the hash algorithms you wish to use.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
//note that the csv file will be derived from the path provided into multiple files (one for each algorithm)
|
||||||
|
const computer = new ComputeHashIndexCache("path/to/your/indexfile/index.csv", [CID_ALGORITHM_NAMES.sha256, CID_ALGORITHM_NAMES.sha1]);
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Compute Hashes**: Invoke the `computeMissingHash` method for each file you wish to hash. This method will automatically check the index for existing hashes and compute missing ones.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
let hash:MultiHashData = {};
|
||||||
|
await computer.computeMissingHash("path/to/your/file.txt", hash);
|
||||||
|
console.log(hash[CID_ALGORITHM_NAMES.sha256]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example of Index File
|
||||||
|
|
||||||
|
An example index file (`index-cid_sha2-256.csv`) is shown below, demonstrating how file paths, sizes, modification times, and computed hashes are stored:
|
||||||
|
|
||||||
|
```
|
||||||
|
path,size,mtime,cid_sha2-256
|
||||||
|
file.mkv,230265591,2023-01-17T18:53:37.692Z,baejbeif45glg263244x7cbjzyd3ssp5edzae7bxurzxjhnogazcwmj32wa
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions to Meta-Hash are welcome! Please read our contributing guidelines for more information on how to participate.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Meta-Hash is released under the [MIT License](LICENSE.md).
|
||||||
|
|||||||
+21
-12
@@ -1,35 +1,44 @@
|
|||||||
import { BinaryLike } from 'crypto';
|
|
||||||
|
|
||||||
interface MultiHashData {
|
interface MultiHashData {
|
||||||
"cid_sha2-256"?: string;
|
"cid_sha2-256"?: string;
|
||||||
cid_sha1?: string;
|
cid_sha1?: string;
|
||||||
cid_md5?: string;
|
cid_md5?: string;
|
||||||
"cid_sha3-256"?: string;
|
"cid_sha3-256"?: string;
|
||||||
|
cid_crc32?: string;
|
||||||
}
|
}
|
||||||
interface ComputeInterface {
|
interface ComputeInterface {
|
||||||
computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void>;
|
computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void>;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* according to
|
||||||
|
* https://ipfs.io/ipfs/QmXec1jjwzxWJoNbxQF5KffL8q6hFXm9QwUGaa3wKGk6dT/#title=Multicodecs&src=https://raw.githubusercontent.com/multiformats/multicodec/master/table.csv
|
||||||
|
*/
|
||||||
declare enum CID_ALGORITHM_CODES {
|
declare enum CID_ALGORITHM_CODES {
|
||||||
sha256 = 18,
|
crc32 = 306,
|
||||||
sha1 = 17,
|
|
||||||
md5 = 213,
|
md5 = 213,
|
||||||
sha3_256 = 22
|
sha1 = 17,
|
||||||
|
sha256 = 18,
|
||||||
|
sha3_256 = 22,
|
||||||
|
sha3_384 = 21
|
||||||
}
|
}
|
||||||
declare enum CID_ALGORITHM_NAMES {
|
declare enum CID_ALGORITHM_NAMES {
|
||||||
sha256 = "cid_sha2-256",
|
crc32 = "cid_crc32",
|
||||||
sha1 = "cid_sha1",
|
|
||||||
md5 = "cid_md5",
|
md5 = "cid_md5",
|
||||||
sha3_256 = "cid_sha3-256"
|
sha1 = "cid_sha1",
|
||||||
|
sha256 = "cid_sha2-256",
|
||||||
|
sha3_256 = "cid_sha3-256",
|
||||||
|
sha3_384 = "cid_sha3-384"
|
||||||
}
|
}
|
||||||
declare enum CID_ALGORITHM {
|
declare enum CID_ALGORITHM {
|
||||||
sha256 = "sha2-256",
|
crc32 = "crc32",
|
||||||
sha1 = "sha1",
|
|
||||||
md5 = "md5",
|
md5 = "md5",
|
||||||
sha3_256 = "sha3-256"
|
sha1 = "sha1",
|
||||||
|
sha256 = "sha2-256",
|
||||||
|
sha3_256 = "sha3-256",
|
||||||
|
sha3_384 = "sha3-384"
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SimpleHash {
|
interface SimpleHash {
|
||||||
update(data: BinaryLike): any;
|
update(data: Buffer): any;
|
||||||
digest(): Buffer;
|
digest(): Buffer;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
+21
-12
@@ -1,35 +1,44 @@
|
|||||||
import { BinaryLike } from 'crypto';
|
|
||||||
|
|
||||||
interface MultiHashData {
|
interface MultiHashData {
|
||||||
"cid_sha2-256"?: string;
|
"cid_sha2-256"?: string;
|
||||||
cid_sha1?: string;
|
cid_sha1?: string;
|
||||||
cid_md5?: string;
|
cid_md5?: string;
|
||||||
"cid_sha3-256"?: string;
|
"cid_sha3-256"?: string;
|
||||||
|
cid_crc32?: string;
|
||||||
}
|
}
|
||||||
interface ComputeInterface {
|
interface ComputeInterface {
|
||||||
computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void>;
|
computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void>;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* according to
|
||||||
|
* https://ipfs.io/ipfs/QmXec1jjwzxWJoNbxQF5KffL8q6hFXm9QwUGaa3wKGk6dT/#title=Multicodecs&src=https://raw.githubusercontent.com/multiformats/multicodec/master/table.csv
|
||||||
|
*/
|
||||||
declare enum CID_ALGORITHM_CODES {
|
declare enum CID_ALGORITHM_CODES {
|
||||||
sha256 = 18,
|
crc32 = 306,
|
||||||
sha1 = 17,
|
|
||||||
md5 = 213,
|
md5 = 213,
|
||||||
sha3_256 = 22
|
sha1 = 17,
|
||||||
|
sha256 = 18,
|
||||||
|
sha3_256 = 22,
|
||||||
|
sha3_384 = 21
|
||||||
}
|
}
|
||||||
declare enum CID_ALGORITHM_NAMES {
|
declare enum CID_ALGORITHM_NAMES {
|
||||||
sha256 = "cid_sha2-256",
|
crc32 = "cid_crc32",
|
||||||
sha1 = "cid_sha1",
|
|
||||||
md5 = "cid_md5",
|
md5 = "cid_md5",
|
||||||
sha3_256 = "cid_sha3-256"
|
sha1 = "cid_sha1",
|
||||||
|
sha256 = "cid_sha2-256",
|
||||||
|
sha3_256 = "cid_sha3-256",
|
||||||
|
sha3_384 = "cid_sha3-384"
|
||||||
}
|
}
|
||||||
declare enum CID_ALGORITHM {
|
declare enum CID_ALGORITHM {
|
||||||
sha256 = "sha2-256",
|
crc32 = "crc32",
|
||||||
sha1 = "sha1",
|
|
||||||
md5 = "md5",
|
md5 = "md5",
|
||||||
sha3_256 = "sha3-256"
|
sha1 = "sha1",
|
||||||
|
sha256 = "sha2-256",
|
||||||
|
sha3_256 = "sha3-256",
|
||||||
|
sha3_384 = "sha3-384"
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SimpleHash {
|
interface SimpleHash {
|
||||||
update(data: BinaryLike): any;
|
update(data: Buffer): any;
|
||||||
digest(): Buffer;
|
digest(): Buffer;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var crypto = require('crypto');
|
||||||
|
var cid = require('multiformats/cid');
|
||||||
|
var digest = require('multiformats/hashes/digest');
|
||||||
|
var x = require('crc-32');
|
||||||
|
|
||||||
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
||||||
|
|
||||||
|
var x__default = /*#__PURE__*/_interopDefault(x);
|
||||||
|
|
||||||
|
var o=(s=>(s[s.crc32=306]="crc32",s[s.md5=213]="md5",s[s.sha1=17]="sha1",s[s.sha256=18]="sha256",s[s.sha3_256=22]="sha3_256",s[s.sha3_384=21]="sha3_384",s))(o||{}),n=(s=>(s.crc32="cid_crc32",s.md5="cid_md5",s.sha1="cid_sha1",s.sha256="cid_sha2-256",s.sha3_256="cid_sha3-256",s.sha3_384="cid_sha3-384",s))(n||{}),u=(s=>(s.crc32="crc32",s.md5="md5",s.sha1="sha1",s.sha256="sha2-256",s.sha3_256="sha3-256",s.sha3_384="sha3-384",s))(u||{});var t=class{_crc=void 0;update(a){return this._crc=x__default.default.buf(a,this._crc),this}digest(){console.log((this._crc>>>0).toString(16));let a=Buffer.alloc(4);return a.writeInt32BE(this._crc,0),a}};async function B({filePath:c,algorithms:a}){let h={"cid_sha2-256":{code:18,hasher:crypto.createHash("sha256")},cid_sha1:{code:17,hasher:crypto.createHash("sha1")},cid_md5:{code:213,hasher:crypto.createHash("md5")},"cid_sha3-256":{code:22,hasher:crypto.createHash("sha3-256")},"cid_sha3-384":{code:21,hasher:crypto.createHash("sha3-384")},cid_crc32:{code:306,hasher:new t}},i=a.map(s=>({hasher:h[s].hasher,code:h[s].code})),d=fs.createReadStream(c);for await(let s of d)for(let r of i)r.hasher.update(s);return await Promise.all(i.map(async({code:s,hasher:r})=>{let m=r.digest(),f=digest.create(s,m);return cid.CID.createV1(s,f).toString()}))}
|
||||||
|
|
||||||
|
exports.a = o;
|
||||||
|
exports.b = n;
|
||||||
|
exports.c = u;
|
||||||
|
exports.d = B;
|
||||||
|
//# sourceMappingURL=out.js.map
|
||||||
|
//# sourceMappingURL=chunk-A5PD5ID4.cjs.map
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
-15
@@ -1,15 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var crypto = require('crypto');
|
|
||||||
var cid = require('multiformats/cid');
|
|
||||||
var digest = require('multiformats/hashes/digest');
|
|
||||||
|
|
||||||
var i=(a=>(a[a.sha256=18]="sha256",a[a.sha1=17]="sha1",a[a.md5=213]="md5",a[a.sha3_256=22]="sha3_256",a))(i||{}),o=(a=>(a.sha256="cid_sha2-256",a.sha1="cid_sha1",a.md5="cid_md5",a.sha3_256="cid_sha3-256",a))(o||{}),f=(a=>(a.sha256="sha2-256",a.sha1="sha1",a.md5="md5",a.sha3_256="sha3-256",a))(f||{});async function x({filePath:c,algorithms:m}){let r={"cid_sha2-256":{code:18,hasher:crypto.createHash("sha256")},cid_sha1:{code:17,hasher:crypto.createHash("sha1")},cid_md5:{code:213,hasher:crypto.createHash("md5")},"cid_sha3-256":{code:22,hasher:crypto.createHash("sha3-256")}},h=m.map(s=>({hasher:r[s].hasher,code:r[s].code})),a=fs.createReadStream(c);for await(let s of a)for(let t of h)t.hasher.update(s);return await Promise.all(h.map(async({code:s,hasher:t})=>{let d=t.digest(),n=digest.create(s,d);return cid.CID.createV1(s,n).toString()}))}
|
|
||||||
|
|
||||||
exports.a = i;
|
|
||||||
exports.b = o;
|
|
||||||
exports.c = f;
|
|
||||||
exports.d = x;
|
|
||||||
//# sourceMappingURL=out.js.map
|
|
||||||
//# sourceMappingURL=chunk-DL4KI2PZ.cjs.map
|
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
{"version":3,"sources":["../src/lib/fileID/ShaComputeWorker.ts","../src/lib/MultiHashData.ts"],"names":["createReadStream","createHash","CID","create","CID_ALGORITHM_CODES","CID_ALGORITHM_NAMES","CID_ALGORITHM","computeCIDs","filePath","algorithms","hashFunctions","hashers","algo","stream","chunk","item","code","hasher","hashBuffer","digest"],"mappings":"AAAA,OAAS,oBAAAA,MAAwB,KACjC,OAAoB,cAAAC,MAAiB,SACrC,OAAS,OAAAC,MAAW,mBACpB,OAAS,UAAAC,MAAc,6BCShB,IAAKC,OACRA,IAAA,OAAS,IAAT,SACAA,IAAA,KAAO,IAAP,OACAA,IAAA,IAAM,KAAN,MACAA,IAAA,SAAW,IAAX,WAJQA,OAAA,IAQAC,OACRA,EAAA,OAAS,eACTA,EAAA,KAAO,WACPA,EAAA,IAAM,UACNA,EAAA,SAAW,eAJHA,OAAA,IAQAC,OACRA,EAAA,OAAS,WACTA,EAAA,KAAO,OACPA,EAAA,IAAM,MACNA,EAAA,SAAW,WAJHA,OAAA,IDVZ,eAAOC,EAAmC,CAAE,SAAAC,EAAU,WAAAC,CAAW,EAA+E,CAC5I,IAAMC,EACA,CACD,eAA6B,CAAE,QAAkC,OAAQT,EAAW,QAAQ,CAAE,EAC9F,SAA2B,CAAE,QAAgC,OAAQA,EAAW,MAAM,CAAE,EACxF,QAA0B,CAAE,SAA+B,OAAQA,EAAW,KAAK,CAAE,EACrF,eAA+B,CAAE,QAAoC,OAAOA,EAAW,UAAU,CAAE,CACxG,EAEMU,EAAUF,EAAW,IAAIG,IAAS,CACpC,OAAQF,EAAcE,CAAI,EAAE,OAC5B,KAAMF,EAAcE,CAAI,EAAE,IAC9B,EAAE,EAEIC,EAASb,EAAiBQ,CAAQ,EACxC,cAAiBM,KAASD,EACtB,QAAWE,KAAQJ,EACfI,EAAK,OAAO,OAAOD,CAAK,EAUhC,OANa,MAAM,QAAQ,IAAIH,EAAQ,IAAI,MAAO,CAAE,KAAAK,EAAM,OAAAC,CAAO,IAAM,CACnE,IAAMC,EAAaD,EAAO,OAAO,EAC3BE,EAAShB,EAAOa,EAAME,CAAU,EACtC,OAAOhB,EAAI,SAASc,EAAMG,CAAM,EAAE,SAAS,CAC/C,CAAC,CAAC,CAGN","sourcesContent":["import { createReadStream } from \"fs\";\nimport {BinaryLike, createHash} from 'crypto';\nimport { CID } from 'multiformats/cid';\nimport { create } from \"multiformats/hashes/digest\";\nimport {CID_ALGORITHM, CID_ALGORITHM_CODES, CID_ALGORITHM_NAMES} from \"../MultiHashData\";\n\nexport interface SimpleHash {\n update(data: BinaryLike),\n digest(): Buffer;\n}\n\n\n/**\n * Compute the CIDs of a file using specific algorithms\n * @param filePath The path to the file\n * @param algorithms Array of algorithms ('sha256', 'sha1')\n * @returns Array of CIDs (in the order of the algorithms)\n */\nexport default async function computeCIDs({ filePath, algorithms }: { filePath: string; algorithms: CID_ALGORITHM_NAMES[] }): Promise<string[]> {\n const hashFunctions: Record<string, { code: number, hasher: SimpleHash }>\n = {\n [CID_ALGORITHM_NAMES.sha256]: { code: CID_ALGORITHM_CODES.sha256, hasher: createHash('sha256') },\n [CID_ALGORITHM_NAMES.sha1]: { code: CID_ALGORITHM_CODES.sha1, hasher: createHash('sha1') },\n [CID_ALGORITHM_NAMES.md5]: { code: CID_ALGORITHM_CODES.md5, hasher: createHash('md5') },\n [CID_ALGORITHM_NAMES.sha3_256]: { code: CID_ALGORITHM_CODES.sha3_256, hasher:createHash('sha3-256') }\n };\n\n const hashers = algorithms.map(algo => ({\n hasher: hashFunctions[algo].hasher,\n code: hashFunctions[algo].code\n }));\n\n const stream = createReadStream(filePath);\n for await (const chunk of stream) {\n for (const item of hashers) {\n item.hasher.update(chunk);\n }\n }\n\n const cids = await Promise.all(hashers.map(async ({ code, hasher }) => {\n const hashBuffer = hasher.digest();\n const digest = create(code, hashBuffer);\n return CID.createV1(code, digest).toString();\n }));\n\n return cids;\n}\n","export interface MultiHashData {\n \"cid_sha2-256\"?: string;//0x12 sha2-256\n cid_sha1?: string; // 0x11 sha1\n cid_md5?: string; // 0xd5 md5\n \"cid_sha3-256\"?: string; // 0x16 sha3-256\n //cid_crc32?: string; //0x0132 CRC32 (SFV) TODO\n}\n\nexport interface ComputeInterface {\n computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void>;\n}\n\nexport enum CID_ALGORITHM_CODES {\n sha256 = 0x12,\n sha1 = 0x11,\n md5 = 0xd5,\n sha3_256 = 0x16,\n //crc32 = 0x0132\n}\n\nexport enum CID_ALGORITHM_NAMES {\n sha256 = 'cid_sha2-256',\n sha1 = 'cid_sha1',\n md5 = 'cid_md5',\n sha3_256 = 'cid_sha3-256',\n //crc32 = 'cid_crc32'\n}\n\nexport enum CID_ALGORITHM {\n sha256 = 'sha2-256',\n sha1 = 'sha1',\n md5 = 'md5',\n sha3_256 = 'sha3-256',\n //crc32 = 'crc32'\n}"]}
|
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
import { createReadStream } from 'fs';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
import { CID } from 'multiformats/cid';
|
||||||
|
import { create } from 'multiformats/hashes/digest';
|
||||||
|
import x from 'crc-32';
|
||||||
|
|
||||||
|
var o=(s=>(s[s.crc32=306]="crc32",s[s.md5=213]="md5",s[s.sha1=17]="sha1",s[s.sha256=18]="sha256",s[s.sha3_256=22]="sha3_256",s[s.sha3_384=21]="sha3_384",s))(o||{}),n=(s=>(s.crc32="cid_crc32",s.md5="cid_md5",s.sha1="cid_sha1",s.sha256="cid_sha2-256",s.sha3_256="cid_sha3-256",s.sha3_384="cid_sha3-384",s))(n||{}),u=(s=>(s.crc32="crc32",s.md5="md5",s.sha1="sha1",s.sha256="sha2-256",s.sha3_256="sha3-256",s.sha3_384="sha3-384",s))(u||{});var t=class{_crc=void 0;update(a){return this._crc=x.buf(a,this._crc),this}digest(){console.log((this._crc>>>0).toString(16));let a=Buffer.alloc(4);return a.writeInt32BE(this._crc,0),a}};async function B({filePath:c,algorithms:a}){let h={"cid_sha2-256":{code:18,hasher:createHash("sha256")},cid_sha1:{code:17,hasher:createHash("sha1")},cid_md5:{code:213,hasher:createHash("md5")},"cid_sha3-256":{code:22,hasher:createHash("sha3-256")},"cid_sha3-384":{code:21,hasher:createHash("sha3-384")},cid_crc32:{code:306,hasher:new t}},i=a.map(s=>({hasher:h[s].hasher,code:h[s].code})),d=createReadStream(c);for await(let s of d)for(let r of i)r.hasher.update(s);return await Promise.all(i.map(async({code:s,hasher:r})=>{let m=r.digest(),f=create(s,m);return CID.createV1(s,f).toString()}))}
|
||||||
|
|
||||||
|
export { o as a, n as b, u as c, B as d };
|
||||||
|
//# sourceMappingURL=out.js.map
|
||||||
|
//# sourceMappingURL=chunk-FGATABDV.js.map
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
-10
@@ -1,10 +0,0 @@
|
|||||||
import { createReadStream } from 'fs';
|
|
||||||
import { createHash } from 'crypto';
|
|
||||||
import { CID } from 'multiformats/cid';
|
|
||||||
import { create } from 'multiformats/hashes/digest';
|
|
||||||
|
|
||||||
var i=(a=>(a[a.sha256=18]="sha256",a[a.sha1=17]="sha1",a[a.md5=213]="md5",a[a.sha3_256=22]="sha3_256",a))(i||{}),o=(a=>(a.sha256="cid_sha2-256",a.sha1="cid_sha1",a.md5="cid_md5",a.sha3_256="cid_sha3-256",a))(o||{}),f=(a=>(a.sha256="sha2-256",a.sha1="sha1",a.md5="md5",a.sha3_256="sha3-256",a))(f||{});async function x({filePath:c,algorithms:m}){let r={"cid_sha2-256":{code:18,hasher:createHash("sha256")},cid_sha1:{code:17,hasher:createHash("sha1")},cid_md5:{code:213,hasher:createHash("md5")},"cid_sha3-256":{code:22,hasher:createHash("sha3-256")}},h=m.map(s=>({hasher:r[s].hasher,code:r[s].code})),a=createReadStream(c);for await(let s of a)for(let t of h)t.hasher.update(s);return await Promise.all(h.map(async({code:s,hasher:t})=>{let d=t.digest(),n=create(s,d);return CID.createV1(s,n).toString()}))}
|
|
||||||
|
|
||||||
export { i as a, o as b, f as c, x as d };
|
|
||||||
//# sourceMappingURL=out.js.map
|
|
||||||
//# sourceMappingURL=chunk-VGBIRFOR.js.map
|
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
{"version":3,"sources":["../src/lib/fileID/ShaComputeWorker.ts","../src/lib/MultiHashData.ts"],"names":["createReadStream","createHash","CID","create","CID_ALGORITHM_CODES","CID_ALGORITHM_NAMES","CID_ALGORITHM","computeCIDs","filePath","algorithms","hashFunctions","hashers","algo","stream","chunk","item","code","hasher","hashBuffer","digest"],"mappings":"AAAA,OAAS,oBAAAA,MAAwB,KACjC,OAAoB,cAAAC,MAAiB,SACrC,OAAS,OAAAC,MAAW,mBACpB,OAAS,UAAAC,MAAc,6BCShB,IAAKC,OACRA,IAAA,OAAS,IAAT,SACAA,IAAA,KAAO,IAAP,OACAA,IAAA,IAAM,KAAN,MACAA,IAAA,SAAW,IAAX,WAJQA,OAAA,IAQAC,OACRA,EAAA,OAAS,eACTA,EAAA,KAAO,WACPA,EAAA,IAAM,UACNA,EAAA,SAAW,eAJHA,OAAA,IAQAC,OACRA,EAAA,OAAS,WACTA,EAAA,KAAO,OACPA,EAAA,IAAM,MACNA,EAAA,SAAW,WAJHA,OAAA,IDVZ,eAAOC,EAAmC,CAAE,SAAAC,EAAU,WAAAC,CAAW,EAA+E,CAC5I,IAAMC,EACA,CACD,eAA6B,CAAE,QAAkC,OAAQT,EAAW,QAAQ,CAAE,EAC9F,SAA2B,CAAE,QAAgC,OAAQA,EAAW,MAAM,CAAE,EACxF,QAA0B,CAAE,SAA+B,OAAQA,EAAW,KAAK,CAAE,EACrF,eAA+B,CAAE,QAAoC,OAAOA,EAAW,UAAU,CAAE,CACxG,EAEMU,EAAUF,EAAW,IAAIG,IAAS,CACpC,OAAQF,EAAcE,CAAI,EAAE,OAC5B,KAAMF,EAAcE,CAAI,EAAE,IAC9B,EAAE,EAEIC,EAASb,EAAiBQ,CAAQ,EACxC,cAAiBM,KAASD,EACtB,QAAWE,KAAQJ,EACfI,EAAK,OAAO,OAAOD,CAAK,EAUhC,OANa,MAAM,QAAQ,IAAIH,EAAQ,IAAI,MAAO,CAAE,KAAAK,EAAM,OAAAC,CAAO,IAAM,CACnE,IAAMC,EAAaD,EAAO,OAAO,EAC3BE,EAAShB,EAAOa,EAAME,CAAU,EACtC,OAAOhB,EAAI,SAASc,EAAMG,CAAM,EAAE,SAAS,CAC/C,CAAC,CAAC,CAGN","sourcesContent":["import { createReadStream } from \"fs\";\nimport {BinaryLike, createHash} from 'crypto';\nimport { CID } from 'multiformats/cid';\nimport { create } from \"multiformats/hashes/digest\";\nimport {CID_ALGORITHM, CID_ALGORITHM_CODES, CID_ALGORITHM_NAMES} from \"../MultiHashData\";\n\nexport interface SimpleHash {\n update(data: BinaryLike),\n digest(): Buffer;\n}\n\n\n/**\n * Compute the CIDs of a file using specific algorithms\n * @param filePath The path to the file\n * @param algorithms Array of algorithms ('sha256', 'sha1')\n * @returns Array of CIDs (in the order of the algorithms)\n */\nexport default async function computeCIDs({ filePath, algorithms }: { filePath: string; algorithms: CID_ALGORITHM_NAMES[] }): Promise<string[]> {\n const hashFunctions: Record<string, { code: number, hasher: SimpleHash }>\n = {\n [CID_ALGORITHM_NAMES.sha256]: { code: CID_ALGORITHM_CODES.sha256, hasher: createHash('sha256') },\n [CID_ALGORITHM_NAMES.sha1]: { code: CID_ALGORITHM_CODES.sha1, hasher: createHash('sha1') },\n [CID_ALGORITHM_NAMES.md5]: { code: CID_ALGORITHM_CODES.md5, hasher: createHash('md5') },\n [CID_ALGORITHM_NAMES.sha3_256]: { code: CID_ALGORITHM_CODES.sha3_256, hasher:createHash('sha3-256') }\n };\n\n const hashers = algorithms.map(algo => ({\n hasher: hashFunctions[algo].hasher,\n code: hashFunctions[algo].code\n }));\n\n const stream = createReadStream(filePath);\n for await (const chunk of stream) {\n for (const item of hashers) {\n item.hasher.update(chunk);\n }\n }\n\n const cids = await Promise.all(hashers.map(async ({ code, hasher }) => {\n const hashBuffer = hasher.digest();\n const digest = create(code, hashBuffer);\n return CID.createV1(code, digest).toString();\n }));\n\n return cids;\n}\n","export interface MultiHashData {\n \"cid_sha2-256\"?: string;//0x12 sha2-256\n cid_sha1?: string; // 0x11 sha1\n cid_md5?: string; // 0xd5 md5\n \"cid_sha3-256\"?: string; // 0x16 sha3-256\n //cid_crc32?: string; //0x0132 CRC32 (SFV) TODO\n}\n\nexport interface ComputeInterface {\n computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void>;\n}\n\nexport enum CID_ALGORITHM_CODES {\n sha256 = 0x12,\n sha1 = 0x11,\n md5 = 0xd5,\n sha3_256 = 0x16,\n //crc32 = 0x0132\n}\n\nexport enum CID_ALGORITHM_NAMES {\n sha256 = 'cid_sha2-256',\n sha1 = 'cid_sha1',\n md5 = 'cid_md5',\n sha3_256 = 'cid_sha3-256',\n //crc32 = 'cid_crc32'\n}\n\nexport enum CID_ALGORITHM {\n sha256 = 'sha2-256',\n sha1 = 'sha1',\n md5 = 'md5',\n sha3_256 = 'sha3-256',\n //crc32 = 'crc32'\n}"]}
|
|
||||||
Vendored
+16
-16
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+6
-7
@@ -1,9 +1,8 @@
|
|||||||
import { C as ComputeInterface, a as CID_ALGORITHM_NAMES, M as MultiHashData } from './ShaComputeWorker-DWOF-qaH.cjs';
|
import { C as ComputeInterface, a as CID_ALGORITHM_NAMES, M as MultiHashData } from './ShaComputeWorker-DxWHpwX4.cjs';
|
||||||
export { c as CID_ALGORITHM, b as CID_ALGORITHM_CODES, S as SimpleHash } from './ShaComputeWorker-DWOF-qaH.cjs';
|
export { c as CID_ALGORITHM, b as CID_ALGORITHM_CODES, S as SimpleHash } from './ShaComputeWorker-DxWHpwX4.cjs';
|
||||||
import * as PQueue from 'p-queue';
|
import * as PQueue from 'p-queue';
|
||||||
import PQueue__default from 'p-queue';
|
import PQueue__default from 'p-queue';
|
||||||
import * as p_queue_dist_priority_queue_js from 'p-queue/dist/priority-queue.js';
|
import * as p_queue_dist_priority_queue_js from 'p-queue/dist/priority-queue.js';
|
||||||
import 'crypto';
|
|
||||||
|
|
||||||
declare class HashComputer implements ComputeInterface {
|
declare class HashComputer implements ComputeInterface {
|
||||||
private targetHash;
|
private targetHash;
|
||||||
@@ -19,7 +18,6 @@ interface IndexLine extends Partial<Record<CID_ALGORITHM_NAMES, string>> {
|
|||||||
}
|
}
|
||||||
declare const INDEX_HEADERS: string[];
|
declare const INDEX_HEADERS: string[];
|
||||||
declare class HashIndexManager {
|
declare class HashIndexManager {
|
||||||
private filePath;
|
|
||||||
private targetHash;
|
private targetHash;
|
||||||
private cache;
|
private cache;
|
||||||
private intervalId;
|
private intervalId;
|
||||||
@@ -29,18 +27,19 @@ declare class HashIndexManager {
|
|||||||
private indexOpsInProgress;
|
private indexOpsInProgress;
|
||||||
private hasChanged;
|
private hasChanged;
|
||||||
private initialLoad;
|
private initialLoad;
|
||||||
|
private filePaths;
|
||||||
constructor(filePath: string, targetHash?: CID_ALGORITHM_NAMES[]);
|
constructor(filePath: string, targetHash?: CID_ALGORITHM_NAMES[]);
|
||||||
getCache(): Map<string, IndexLine>;
|
getCache(): Map<string, IndexLine>;
|
||||||
/**
|
/**
|
||||||
* After init consseutively calls to this method will not reload the index
|
* After init consseutively calls to this method will not reload the index
|
||||||
* @param autosave
|
* @param autosave
|
||||||
*/
|
*/
|
||||||
init(autosave?: boolean): Promise<any>;
|
init(autosave?: boolean): Promise<void>;
|
||||||
checkCSVHeaders(csvContent: string): boolean;
|
private checkCSVHeaders;
|
||||||
start(): void;
|
start(): void;
|
||||||
stopAutoSave(): void;
|
stopAutoSave(): void;
|
||||||
private startAutoSave;
|
private startAutoSave;
|
||||||
loadIndex(): Promise<IndexLine[]>;
|
loadIndex(hash: CID_ALGORITHM_NAMES): Promise<IndexLine[]>;
|
||||||
private loadIndexFromCache;
|
private loadIndexFromCache;
|
||||||
private readCsv;
|
private readCsv;
|
||||||
private saveCacheToFile;
|
private saveCacheToFile;
|
||||||
|
|||||||
Vendored
+6
-7
@@ -1,9 +1,8 @@
|
|||||||
import { C as ComputeInterface, a as CID_ALGORITHM_NAMES, M as MultiHashData } from './ShaComputeWorker-DWOF-qaH.js';
|
import { C as ComputeInterface, a as CID_ALGORITHM_NAMES, M as MultiHashData } from './ShaComputeWorker-DxWHpwX4.js';
|
||||||
export { c as CID_ALGORITHM, b as CID_ALGORITHM_CODES, S as SimpleHash } from './ShaComputeWorker-DWOF-qaH.js';
|
export { c as CID_ALGORITHM, b as CID_ALGORITHM_CODES, S as SimpleHash } from './ShaComputeWorker-DxWHpwX4.js';
|
||||||
import * as PQueue from 'p-queue';
|
import * as PQueue from 'p-queue';
|
||||||
import PQueue__default from 'p-queue';
|
import PQueue__default from 'p-queue';
|
||||||
import * as p_queue_dist_priority_queue_js from 'p-queue/dist/priority-queue.js';
|
import * as p_queue_dist_priority_queue_js from 'p-queue/dist/priority-queue.js';
|
||||||
import 'crypto';
|
|
||||||
|
|
||||||
declare class HashComputer implements ComputeInterface {
|
declare class HashComputer implements ComputeInterface {
|
||||||
private targetHash;
|
private targetHash;
|
||||||
@@ -19,7 +18,6 @@ interface IndexLine extends Partial<Record<CID_ALGORITHM_NAMES, string>> {
|
|||||||
}
|
}
|
||||||
declare const INDEX_HEADERS: string[];
|
declare const INDEX_HEADERS: string[];
|
||||||
declare class HashIndexManager {
|
declare class HashIndexManager {
|
||||||
private filePath;
|
|
||||||
private targetHash;
|
private targetHash;
|
||||||
private cache;
|
private cache;
|
||||||
private intervalId;
|
private intervalId;
|
||||||
@@ -29,18 +27,19 @@ declare class HashIndexManager {
|
|||||||
private indexOpsInProgress;
|
private indexOpsInProgress;
|
||||||
private hasChanged;
|
private hasChanged;
|
||||||
private initialLoad;
|
private initialLoad;
|
||||||
|
private filePaths;
|
||||||
constructor(filePath: string, targetHash?: CID_ALGORITHM_NAMES[]);
|
constructor(filePath: string, targetHash?: CID_ALGORITHM_NAMES[]);
|
||||||
getCache(): Map<string, IndexLine>;
|
getCache(): Map<string, IndexLine>;
|
||||||
/**
|
/**
|
||||||
* After init consseutively calls to this method will not reload the index
|
* After init consseutively calls to this method will not reload the index
|
||||||
* @param autosave
|
* @param autosave
|
||||||
*/
|
*/
|
||||||
init(autosave?: boolean): Promise<any>;
|
init(autosave?: boolean): Promise<void>;
|
||||||
checkCSVHeaders(csvContent: string): boolean;
|
private checkCSVHeaders;
|
||||||
start(): void;
|
start(): void;
|
||||||
stopAutoSave(): void;
|
stopAutoSave(): void;
|
||||||
private startAutoSave;
|
private startAutoSave;
|
||||||
loadIndex(): Promise<IndexLine[]>;
|
loadIndex(hash: CID_ALGORITHM_NAMES): Promise<IndexLine[]>;
|
||||||
private loadIndexFromCache;
|
private loadIndexFromCache;
|
||||||
private readCsv;
|
private readCsv;
|
||||||
private saveCacheToFile;
|
private saveCacheToFile;
|
||||||
|
|||||||
Vendored
+6
-6
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var chunkDL4KI2PZ_cjs = require('./chunk-DL4KI2PZ.cjs');
|
var chunkA5PD5ID4_cjs = require('./chunk-A5PD5ID4.cjs');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = chunkDL4KI2PZ_cjs.d;
|
module.exports = chunkA5PD5ID4_cjs.d;
|
||||||
//# sourceMappingURL=out.js.map
|
//# sourceMappingURL=out.js.map
|
||||||
//# sourceMappingURL=worker.cjs.map
|
//# sourceMappingURL=worker.cjs.map
|
||||||
Vendored
+1
-2
@@ -1,2 +1 @@
|
|||||||
import 'crypto';
|
export { S as SimpleHash, d as default } from './ShaComputeWorker-DxWHpwX4.cjs';
|
||||||
export { S as SimpleHash, d as default } from './ShaComputeWorker-DWOF-qaH.cjs';
|
|
||||||
|
|||||||
Vendored
+1
-2
@@ -1,2 +1 @@
|
|||||||
import 'crypto';
|
export { S as SimpleHash, d as default } from './ShaComputeWorker-DxWHpwX4.js';
|
||||||
export { S as SimpleHash, d as default } from './ShaComputeWorker-DWOF-qaH.js';
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
export { d as default } from './chunk-VGBIRFOR.js';
|
export { d as default } from './chunk-FGATABDV.js';
|
||||||
//# sourceMappingURL=out.js.map
|
//# sourceMappingURL=out.js.map
|
||||||
//# sourceMappingURL=worker.js.map
|
//# sourceMappingURL=worker.js.map
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
"packageManager": "[email protected]",
|
"packageManager": "[email protected]",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
|
"crc-32": "^1.2.2",
|
||||||
"csv-parse": "^5.5.5",
|
"csv-parse": "^5.5.5",
|
||||||
"csv-stringify": "^6.4.6",
|
"csv-stringify": "^6.4.6",
|
||||||
"multiformats": "^13.1.0",
|
"multiformats": "^13.1.0",
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export class ComputeHashIndexCache implements ComputeInterface {
|
|||||||
hashComputer: HashComputer;
|
hashComputer: HashComputer;
|
||||||
|
|
||||||
constructor(indexFilePath: string,private targetHash: CID_ALGORITHM_NAMES[] = [CID_ALGORITHM_NAMES.sha1, CID_ALGORITHM_NAMES.sha256],workerPath?:string) {
|
constructor(indexFilePath: string,private targetHash: CID_ALGORITHM_NAMES[] = [CID_ALGORITHM_NAMES.sha1, CID_ALGORITHM_NAMES.sha256],workerPath?:string) {
|
||||||
this.hashComputer = new HashComputer(this.targetHash,workerPath);
|
this.hashComputer = new HashComputer(targetHash,workerPath);
|
||||||
this.hashIndexManager = new HashIndexManager(indexFilePath);
|
this.hashIndexManager = new HashIndexManager(indexFilePath,targetHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
async computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void> {
|
async computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void> {
|
||||||
|
|||||||
+54
-25
@@ -12,6 +12,7 @@ interface IndexLine extends Partial<Record<CID_ALGORITHM_NAMES, string>> {
|
|||||||
size: string;
|
size: string;
|
||||||
mtime: string;
|
mtime: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const INDEX_HEADERS = ['path', 'size', 'mtime'];
|
export const INDEX_HEADERS = ['path', 'size', 'mtime'];
|
||||||
|
|
||||||
export class HashIndexManager {
|
export class HashIndexManager {
|
||||||
@@ -22,11 +23,16 @@ export class HashIndexManager {
|
|||||||
private lastCacheFile: IndexLine[]; //state of the file last time it was read
|
private lastCacheFile: IndexLine[]; //state of the file last time it was read
|
||||||
private indexOpsInProgress: boolean = false;
|
private indexOpsInProgress: boolean = false;
|
||||||
private hasChanged: boolean = false;
|
private hasChanged: boolean = false;
|
||||||
private initialLoad;
|
private initialLoad: Promise<void>;
|
||||||
|
private filePaths: { [key in CID_ALGORITHM_NAMES]?: string } = {};
|
||||||
|
|
||||||
constructor(private filePath: string,private targetHash:CID_ALGORITHM_NAMES[]= [CID_ALGORITHM_NAMES.sha1,CID_ALGORITHM_NAMES.sha256]) {
|
constructor(filePath: string, private targetHash: CID_ALGORITHM_NAMES[] = [CID_ALGORITHM_NAMES.sha1, CID_ALGORITHM_NAMES.sha256]) {
|
||||||
if (!filePath) {
|
filePath = filePath.replace(".csv", "");
|
||||||
throw new Error('Invalid index file path');
|
for (const hash of this.targetHash) {
|
||||||
|
this.filePaths[hash] = `${filePath}-${hash}.csv`;
|
||||||
|
if (!this.filePaths[hash]) {
|
||||||
|
throw new Error(`Invalid index file path for ${hash}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,19 +44,21 @@ export class HashIndexManager {
|
|||||||
* After init consseutively calls to this method will not reload the index
|
* After init consseutively calls to this method will not reload the index
|
||||||
* @param autosave
|
* @param autosave
|
||||||
*/
|
*/
|
||||||
async init(autosave = true) {
|
public async init(autosave = true) {
|
||||||
if (!this.initialLoad) {
|
if (!this.initialLoad) {
|
||||||
this.initialLoad = new Promise<void>(async (resolve, reject) => {
|
this.initialLoad = new Promise<void>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
if (!this.checkCSVHeaders(this.filePath)) {
|
for (const hash of this.targetHash) {
|
||||||
throw new Error('Invalid index file headers');
|
if (!this.checkCSVHeaders(this.filePaths[hash], hash)) {
|
||||||
|
throw new Error(`Invalid index file headers for ${hash}`);
|
||||||
|
}
|
||||||
|
await this.loadIndex(hash);
|
||||||
}
|
}
|
||||||
await this.loadIndex();
|
|
||||||
if (autosave) {
|
if (autosave) {
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
}catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -59,7 +67,7 @@ export class HashIndexManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to check CSV headers
|
// Function to check CSV headers
|
||||||
checkCSVHeaders (csvContent: string): boolean {
|
private checkCSVHeaders(csvContent: string, hash: CID_ALGORITHM_NAMES): boolean {
|
||||||
const records = parseSync(csvContent, {
|
const records = parseSync(csvContent, {
|
||||||
bom: true,
|
bom: true,
|
||||||
columns: true,
|
columns: true,
|
||||||
@@ -76,7 +84,7 @@ export class HashIndexManager {
|
|||||||
|
|
||||||
// Define required headers
|
// Define required headers
|
||||||
//from the IndexLine interface
|
//from the IndexLine interface
|
||||||
const requiredHeaders = ['path', 'size', 'mtime', ...this.targetHash];
|
const requiredHeaders = [...INDEX_HEADERS, hash];
|
||||||
|
|
||||||
// Check if all required headers are present
|
// Check if all required headers are present
|
||||||
return requiredHeaders.every(header => headers.includes(header));
|
return requiredHeaders.every(header => headers.includes(header));
|
||||||
@@ -98,15 +106,21 @@ export class HashIndexManager {
|
|||||||
this.intervalId = setInterval(() => this.saveCacheToFile(), time);
|
this.intervalId = setInterval(() => this.saveCacheToFile(), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadIndex(): Promise<IndexLine[]> {
|
public async loadIndex(hash: CID_ALGORITHM_NAMES): Promise<IndexLine[]> {
|
||||||
if (await existsAsync(this.filePath)) {
|
if (await existsAsync(this.filePaths[hash])) {
|
||||||
// check the file size and if it did not change, do not read the file
|
// check the file size and if it did not change, do not read the file
|
||||||
const stats = await fs.stat(this.filePath);
|
const stats = await fs.stat(this.filePaths[hash]);
|
||||||
if (this.lastIndexFileSize !== stats.size) {
|
if (this.lastIndexFileSize !== stats.size) {
|
||||||
// Read existing file content and parse it
|
// Read existing file content and parse it
|
||||||
const records: IndexLine[] = await this.readCsv();
|
const records: IndexLine[] = await this.readCsv(hash);
|
||||||
for (const record of records) {
|
for (const record of records) {
|
||||||
|
let indexLine = this.cache.get(record.path);
|
||||||
|
if (!indexLine) {
|
||||||
this.cache.set(record.path, record);
|
this.cache.set(record.path, record);
|
||||||
|
} else {
|
||||||
|
//update the cache with the latest data
|
||||||
|
indexLine[hash] = record[hash];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.lastIndexFileSize = stats.size;
|
this.lastIndexFileSize = stats.size;
|
||||||
this.lastCacheFile = records;
|
this.lastCacheFile = records;
|
||||||
@@ -122,8 +136,8 @@ export class HashIndexManager {
|
|||||||
return Array.from(this.cache.values());
|
return Array.from(this.cache.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async readCsv(): Promise<IndexLine[]> {
|
private async readCsv(hash: CID_ALGORITHM_NAMES): Promise<IndexLine[]> {
|
||||||
if (!(await existsAsync(this.filePath))) {
|
if (!(await existsAsync(this.filePaths[hash]))) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
@@ -136,13 +150,13 @@ export class HashIndexManager {
|
|||||||
const records: IndexLine[] = [];
|
const records: IndexLine[] = [];
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
createReadStream(this.filePath)
|
createReadStream(this.filePaths[hash])
|
||||||
.pipe(parser)
|
.pipe(parser)
|
||||||
.on('data', (record) => {
|
.on('data', (record) => {
|
||||||
records.push(record);
|
records.push(record);
|
||||||
}).on('end', () => {
|
}).on('end', () => {
|
||||||
resolve(records);
|
resolve(records);
|
||||||
console.log(`Index read time ${performance.now() - start}ms`);
|
console.log(`Index read ${hash} time ${performance.now() - start}ms`);
|
||||||
}).on('error', (err) => {
|
}).on('error', (err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
@@ -156,8 +170,9 @@ export class HashIndexManager {
|
|||||||
this.hasChanged = false;
|
this.hasChanged = false;
|
||||||
this.indexOpsInProgress = true;
|
this.indexOpsInProgress = true;
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
let existingRows: IndexLine[] = await this.loadIndex();
|
|
||||||
|
|
||||||
|
for (const hash of this.targetHash) {
|
||||||
|
let existingRows: IndexLine[] = await this.loadIndex(hash);
|
||||||
if (this.cache.size !== 0) {
|
if (this.cache.size !== 0) {
|
||||||
const cacheRows: IndexLine[] = this.loadIndexFromCache();
|
const cacheRows: IndexLine[] = this.loadIndexFromCache();
|
||||||
// Filter out cacheRows that are already in the file
|
// Filter out cacheRows that are already in the file
|
||||||
@@ -171,12 +186,13 @@ export class HashIndexManager {
|
|||||||
{key: 'path', header: 'path'},
|
{key: 'path', header: 'path'},
|
||||||
{key: 'size', header: 'size'},
|
{key: 'size', header: 'size'},
|
||||||
{key: 'mtime', header: 'mtime'},
|
{key: 'mtime', header: 'mtime'},
|
||||||
...this.targetHash.map(hash => ({key: hash, header: hash})),
|
{key: hash, header: hash},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Append new cacheRows to the file
|
// Append new cacheRows to the file
|
||||||
await fs.appendFile(this.filePath, csvString);
|
await fs.appendFile(this.filePaths[hash], csvString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const totalTime = performance.now() - start;
|
const totalTime = performance.now() - start;
|
||||||
@@ -192,13 +208,13 @@ export class HashIndexManager {
|
|||||||
public getCidForFile(filePath: string, fileSize: number, mtime: string): IndexLine {
|
public getCidForFile(filePath: string, fileSize: number, mtime: string): IndexLine {
|
||||||
const fileName = path.basename(filePath);
|
const fileName = path.basename(filePath);
|
||||||
let fileNameIndex = this.cache.get(fileName);
|
let fileNameIndex = this.cache.get(fileName);
|
||||||
if(fileNameIndex) {
|
if (fileNameIndex) {
|
||||||
if(fileNameIndex.mtime) {
|
if (fileNameIndex.mtime) {
|
||||||
//if we have a mtime, we need to check it
|
//if we have a mtime, we need to check it
|
||||||
if (fileNameIndex.size === (fileSize + "") && fileNameIndex.mtime === mtime) {
|
if (fileNameIndex.size === (fileSize + "") && fileNameIndex.mtime === mtime) {
|
||||||
return fileNameIndex;
|
return fileNameIndex;
|
||||||
}
|
}
|
||||||
}else {
|
} else {
|
||||||
//mtime is optional
|
//mtime is optional
|
||||||
if (fileNameIndex.size === (fileSize + "")) {
|
if (fileNameIndex.size === (fileSize + "")) {
|
||||||
return fileNameIndex;
|
return fileNameIndex;
|
||||||
@@ -219,10 +235,23 @@ export class HashIndexManager {
|
|||||||
if (!filePath || !fileSize || !mtime || !hashs) {
|
if (!filePath || !fileSize || !mtime || !hashs) {
|
||||||
throw new Error('Invalid parameters');
|
throw new Error('Invalid parameters');
|
||||||
}
|
}
|
||||||
|
for (const hash of this.targetHash) {
|
||||||
|
if (!hashs[hash]) {
|
||||||
|
throw new Error(`Missing hash ${hash}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
const size = fileSize + "";
|
const size = fileSize + "";
|
||||||
const baseName = path.basename(filePath);
|
const baseName = path.basename(filePath);
|
||||||
|
let indexLine = this.cache.get(baseName);
|
||||||
|
if (!indexLine) {
|
||||||
const data = {path: baseName, size: size, mtime: mtime, ...hashs};
|
const data = {path: baseName, size: size, mtime: mtime, ...hashs};
|
||||||
this.cache.set(baseName, data);
|
this.cache.set(baseName, data);
|
||||||
|
} else {
|
||||||
|
//update the cache with the latest data
|
||||||
|
for (const hash of this.targetHash) {
|
||||||
|
indexLine[hash] = hashs[hash];
|
||||||
|
}
|
||||||
|
}
|
||||||
this.hasChanged = true;
|
this.hasChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-10
@@ -3,33 +3,40 @@ export interface MultiHashData {
|
|||||||
cid_sha1?: string; // 0x11 sha1
|
cid_sha1?: string; // 0x11 sha1
|
||||||
cid_md5?: string; // 0xd5 md5
|
cid_md5?: string; // 0xd5 md5
|
||||||
"cid_sha3-256"?: string; // 0x16 sha3-256
|
"cid_sha3-256"?: string; // 0x16 sha3-256
|
||||||
//cid_crc32?: string; //0x0132 CRC32 (SFV) TODO
|
cid_crc32?: string; //0x0132 CRC32 (SFV)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComputeInterface {
|
export interface ComputeInterface {
|
||||||
computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void>;
|
computeMissingHash(filePath: string, metadata: MultiHashData): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* according to
|
||||||
|
* https://ipfs.io/ipfs/QmXec1jjwzxWJoNbxQF5KffL8q6hFXm9QwUGaa3wKGk6dT/#title=Multicodecs&src=https://raw.githubusercontent.com/multiformats/multicodec/master/table.csv
|
||||||
|
*/
|
||||||
export enum CID_ALGORITHM_CODES {
|
export enum CID_ALGORITHM_CODES {
|
||||||
sha256 = 0x12,
|
crc32 = 0x0132,
|
||||||
sha1 = 0x11,
|
|
||||||
md5 = 0xd5,
|
md5 = 0xd5,
|
||||||
|
sha1 = 0x11,
|
||||||
|
sha256 = 0x12,
|
||||||
sha3_256 = 0x16,
|
sha3_256 = 0x16,
|
||||||
//crc32 = 0x0132
|
sha3_384 = 0x15,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CID_ALGORITHM_NAMES {
|
export enum CID_ALGORITHM_NAMES {
|
||||||
sha256 = 'cid_sha2-256',
|
crc32 = 'cid_crc32',
|
||||||
sha1 = 'cid_sha1',
|
|
||||||
md5 = 'cid_md5',
|
md5 = 'cid_md5',
|
||||||
|
sha1 = 'cid_sha1',
|
||||||
|
sha256 = 'cid_sha2-256',
|
||||||
sha3_256 = 'cid_sha3-256',
|
sha3_256 = 'cid_sha3-256',
|
||||||
//crc32 = 'cid_crc32'
|
sha3_384 = 'cid_sha3-384',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CID_ALGORITHM {
|
export enum CID_ALGORITHM {
|
||||||
sha256 = 'sha2-256',
|
crc32 = 'crc32',
|
||||||
sha1 = 'sha1',
|
|
||||||
md5 = 'md5',
|
md5 = 'md5',
|
||||||
|
sha1 = 'sha1',
|
||||||
|
sha256 = 'sha2-256',
|
||||||
sha3_256 = 'sha3-256',
|
sha3_256 = 'sha3-256',
|
||||||
//crc32 = 'crc32'
|
sha3_384 = 'sha3-384',
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,33 @@
|
|||||||
import { createReadStream } from "fs";
|
import {createReadStream} from "fs";
|
||||||
import {BinaryLike, createHash} from 'crypto';
|
import {createHash} from 'crypto';
|
||||||
import { CID } from 'multiformats/cid';
|
import {CID} from 'multiformats/cid';
|
||||||
import { create } from "multiformats/hashes/digest";
|
import {create} from "multiformats/hashes/digest";
|
||||||
import {CID_ALGORITHM, CID_ALGORITHM_CODES, CID_ALGORITHM_NAMES} from "../MultiHashData";
|
import crc32 from 'crc-32';
|
||||||
|
import {CID_ALGORITHM_CODES, CID_ALGORITHM_NAMES} from "../MultiHashData";
|
||||||
|
|
||||||
export interface SimpleHash {
|
export interface SimpleHash {
|
||||||
update(data: BinaryLike),
|
update(data: Buffer),
|
||||||
|
|
||||||
digest(): Buffer;
|
digest(): Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Crc32Hash implements SimpleHash {
|
||||||
|
private _crc: number = undefined;
|
||||||
|
|
||||||
|
update(data: Buffer): Crc32Hash {
|
||||||
|
// Update the CRC-32 checksum with the new chunk of data
|
||||||
|
this._crc = crc32.buf(data, this._crc);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
digest(): Buffer {
|
||||||
|
console.log((this._crc>>>0).toString(16)) ;
|
||||||
|
const buffer = Buffer.alloc(4); // Create a buffer of 4 bytes (32 bits)
|
||||||
|
buffer.writeInt32BE(this._crc, 0); // Write the unsigned integer to the buffer in big-endian format
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the CIDs of a file using specific algorithms
|
* Compute the CIDs of a file using specific algorithms
|
||||||
@@ -16,13 +35,19 @@ export interface SimpleHash {
|
|||||||
* @param algorithms Array of algorithms ('sha256', 'sha1')
|
* @param algorithms Array of algorithms ('sha256', 'sha1')
|
||||||
* @returns Array of CIDs (in the order of the algorithms)
|
* @returns Array of CIDs (in the order of the algorithms)
|
||||||
*/
|
*/
|
||||||
export default async function computeCIDs({ filePath, algorithms }: { filePath: string; algorithms: CID_ALGORITHM_NAMES[] }): Promise<string[]> {
|
export default async function computeCIDs({filePath, algorithms}: {
|
||||||
|
filePath: string;
|
||||||
|
algorithms: CID_ALGORITHM_NAMES[]
|
||||||
|
}): Promise<string[]> {
|
||||||
const hashFunctions: Record<string, { code: number, hasher: SimpleHash }>
|
const hashFunctions: Record<string, { code: number, hasher: SimpleHash }>
|
||||||
= {
|
= {
|
||||||
[CID_ALGORITHM_NAMES.sha256]: { code: CID_ALGORITHM_CODES.sha256, hasher: createHash('sha256') },
|
[CID_ALGORITHM_NAMES.sha256]: {code: CID_ALGORITHM_CODES.sha256, hasher: createHash('sha256')},
|
||||||
[CID_ALGORITHM_NAMES.sha1]: { code: CID_ALGORITHM_CODES.sha1, hasher: createHash('sha1') },
|
[CID_ALGORITHM_NAMES.sha1]: {code: CID_ALGORITHM_CODES.sha1, hasher: createHash('sha1')},
|
||||||
[CID_ALGORITHM_NAMES.md5]: { code: CID_ALGORITHM_CODES.md5, hasher: createHash('md5') },
|
[CID_ALGORITHM_NAMES.md5]: {code: CID_ALGORITHM_CODES.md5, hasher: createHash('md5')},
|
||||||
[CID_ALGORITHM_NAMES.sha3_256]: { code: CID_ALGORITHM_CODES.sha3_256, hasher:createHash('sha3-256') }
|
[CID_ALGORITHM_NAMES.sha3_256]: {code: CID_ALGORITHM_CODES.sha3_256, hasher: createHash('sha3-256')},
|
||||||
|
[CID_ALGORITHM_NAMES.sha3_384]: {code: CID_ALGORITHM_CODES.sha3_384, hasher: createHash('sha3-384')},
|
||||||
|
[CID_ALGORITHM_NAMES.crc32]: {code: CID_ALGORITHM_CODES.crc32, hasher: new Crc32Hash()}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const hashers = algorithms.map(algo => ({
|
const hashers = algorithms.map(algo => ({
|
||||||
@@ -33,11 +58,11 @@ export default async function computeCIDs({ filePath, algorithms }: { filePath:
|
|||||||
const stream = createReadStream(filePath);
|
const stream = createReadStream(filePath);
|
||||||
for await (const chunk of stream) {
|
for await (const chunk of stream) {
|
||||||
for (const item of hashers) {
|
for (const item of hashers) {
|
||||||
item.hasher.update(chunk);
|
item.hasher.update(chunk as Buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cids = await Promise.all(hashers.map(async ({ code, hasher }) => {
|
const cids = await Promise.all(hashers.map(async ({code, hasher}) => {
|
||||||
const hashBuffer = hasher.digest();
|
const hashBuffer = hasher.digest();
|
||||||
const digest = create(code, hashBuffer);
|
const digest = create(code, hashBuffer);
|
||||||
return CID.createV1(code, digest).toString();
|
return CID.createV1(code, digest).toString();
|
||||||
|
|||||||
+23
-8
@@ -1,27 +1,42 @@
|
|||||||
import {HashComputer} from "../lib";
|
import {CID_ALGORITHM_NAMES, HashComputer} from "../lib";
|
||||||
import {CID_ALGORITHM_NAMES} from "../lib";
|
|
||||||
|
|
||||||
import {describe, it, expect} from 'vitest';
|
import {describe, expect, it} from 'vitest';
|
||||||
import * as process from "process";
|
import * as process from "process";
|
||||||
|
|
||||||
process.env.WORKER_URL = "./dist/worker.js";
|
process.env.WORKER_URL = "./dist/worker.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* /!\ WARNING /!\
|
||||||
|
* Remember to build the worker each time you change the worker code.
|
||||||
|
*/
|
||||||
|
|
||||||
describe('add', () => {
|
describe('add', () => {
|
||||||
it('meta data compute', async () => {
|
it('meta data compute', async () => {
|
||||||
let hashComputer = new HashComputer([
|
let hashComputer = new HashComputer([
|
||||||
CID_ALGORITHM_NAMES.sha1,
|
CID_ALGORITHM_NAMES.sha1,
|
||||||
CID_ALGORITHM_NAMES.sha256,
|
CID_ALGORITHM_NAMES.sha256,
|
||||||
CID_ALGORITHM_NAMES.md5,
|
CID_ALGORITHM_NAMES.md5,
|
||||||
CID_ALGORITHM_NAMES.sha3_256
|
CID_ALGORITHM_NAMES.sha3_256,
|
||||||
|
CID_ALGORITHM_NAMES.crc32,
|
||||||
|
CID_ALGORITHM_NAMES.sha3_384
|
||||||
]);
|
]);
|
||||||
let metadata = {};
|
let metadata = {};
|
||||||
await hashComputer.computeMissingHash('./src/tests/test.txt', metadata);
|
await hashComputer.computeMissingHash('./src/tests/test.txt', metadata);
|
||||||
console.log(metadata);
|
console.log(metadata);
|
||||||
|
/**
|
||||||
|
* test.txt CF20B62A (CRC32)
|
||||||
|
* 26f1157f4349a6fb5d207ac5f5995140 *test.txt (MD5)
|
||||||
|
* bb242b7b821074f4b9eb9a94d439ab668705aac4 ?SHA1*test.txt
|
||||||
|
* 2aa7a80d2fd1eb96d31afc7cdda9211a97524202c63f04151d6163fba35061a0 ?SHA256*test.txt
|
||||||
|
* 865a1ea4977b25e04b8ea84af93b21f1a031cb48359f3805089cdae1d89842b9a3faf87654989dcae418144a8bbb378c ?SHA3-384*test.txt
|
||||||
|
*/
|
||||||
expect(metadata).toEqual({
|
expect(metadata).toEqual({
|
||||||
cid_sha1: 'baeircff3eqvxxaqqot2lt242stkdtk3gq4c2vra',
|
"cid_crc32": "bagzafmqcathsbnrk",
|
||||||
'cid_sha2-256': 'baejbeibku6ua2l6r5olnggx4pto2sii2s5jeeawgh4cbkhlbmp52gudbua',
|
"cid_sha1": 'baeircff3eqvxxaqqot2lt242stkdtk3gq4c2vra',
|
||||||
cid_md5: 'bahkqdvibcatpcfl7ine2n625eb5ml5mzkfaa',
|
"cid_sha2-256": 'baejbeibku6ua2l6r5olnggx4pto2sii2s5jeeawgh4cbkhlbmp52gudbua',
|
||||||
'cid_sha3-256': 'baelbmidner7x3dd7t2kldh6wiq4w6lsye7qrwo6uoqot2t52zkhqk56fnq'
|
"cid_md5": 'bahkqdvibcatpcfl7ine2n625eb5ml5mzkfaa',
|
||||||
|
"cid_sha3-256": 'baelbmidner7x3dd7t2kldh6wiq4w6lsye7qrwo6uoqot2t52zkhqk56fnq',
|
||||||
|
"cid_sha3-384": 'baekrkmeglipkjf33exqexdvijl4twipruay4wsbvt44akce43lq5rgccxgr7v6dwksmj3sxedakevc53g6ga'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -417,6 +417,7 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node": "npm:^20.12.2"
|
"@types/node": "npm:^20.12.2"
|
||||||
chokidar: "npm:^3.6.0"
|
chokidar: "npm:^3.6.0"
|
||||||
|
crc-32: "npm:^1.2.2"
|
||||||
csv-parse: "npm:^5.5.5"
|
csv-parse: "npm:^5.5.5"
|
||||||
csv-stringify: "npm:^6.4.6"
|
csv-stringify: "npm:^6.4.6"
|
||||||
multiformats: "npm:^13.1.0"
|
multiformats: "npm:^13.1.0"
|
||||||
@@ -962,6 +963,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"crc-32@npm:^1.2.2":
|
||||||
|
version: 1.2.2
|
||||||
|
resolution: "crc-32@npm:1.2.2"
|
||||||
|
bin:
|
||||||
|
crc32: bin/crc32.njs
|
||||||
|
checksum: 10c0/11dcf4a2e77ee793835d49f2c028838eae58b44f50d1ff08394a610bfd817523f105d6ae4d9b5bef0aad45510f633eb23c903e9902e4409bed1ce70cb82b9bf0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"create-require@npm:^1.1.0":
|
"create-require@npm:^1.1.0":
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
resolution: "create-require@npm:1.1.1"
|
resolution: "create-require@npm:1.1.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user