@ocfl/ocfl 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +72 -97
- package/index.mjs +3 -3
- package/lib/digest.js +245 -77
- package/lib/enum.js +73 -75
- package/lib/extension.js +4 -16
- package/lib/extensions/0003-hash-and-id-n-tuple-storage-layout.js +5 -3
- package/lib/extensions/0004-hashed-n-tuple-storage-layout.js +6 -4
- package/lib/extensions/0009-digest-algorithms.js +18 -0
- package/lib/extensions/index.js +1 -1
- package/lib/file.js +129 -0
- package/lib/index.js +0 -12
- package/lib/inventory.js +294 -101
- package/lib/object.js +287 -492
- package/lib/storage.js +23 -14
- package/lib/store.js +26 -18
- package/lib/transaction.js +177 -142
- package/lib/types.d.ts +75 -16
- package/lib/utils.js +52 -21
- package/lib/validation.js +1 -1
- package/package.json +11 -8
- package/lib/extensions/0001-digest-algorithms.js +0 -46
- package/lib/temp.js +0 -264
package/index.js
CHANGED
|
@@ -3,22 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const { OcflConstants, OcflDigest, OcflExtension, OcflStorageLayout, OcflStore,
|
|
6
|
-
OcflObject,
|
|
7
|
-
OcflObjectInventory, OcflObjectTransactionImpl,
|
|
6
|
+
OcflObject, OcflObjectInventory, OcflObjectTransactionImpl,
|
|
8
7
|
OcflStorage, OcflStorageImpl,
|
|
9
|
-
createObjectProxy, createTransactionProxy,
|
|
10
8
|
extensions
|
|
11
9
|
} = require('./lib/index.js');
|
|
12
10
|
|
|
13
|
-
// const { FlatDirectStorageLayout,
|
|
14
|
-
// FlatOmitPrefixStorageLayout,
|
|
15
|
-
// HashAndIdNTupleStorageLayout,
|
|
16
|
-
// HashedNTupleStorageLayout,
|
|
17
|
-
// NTupleOmitPrefixStorageLayout } = extensions;
|
|
18
|
-
|
|
19
|
-
/** @typedef {import('./lib/object.js').OcflObjectConfig} OcflObjectConfig */
|
|
20
|
-
/** @typedef {import('./lib/storage.js').OcflStorageConfig} OcflStorageConfig */
|
|
21
|
-
|
|
22
11
|
/**
|
|
23
12
|
* @typedef {Object} StorageLayout
|
|
24
13
|
* @property {typeof extensions.FlatDirectStorageLayout} FlatDirectStorageLayout
|
|
@@ -28,7 +17,10 @@ const { OcflConstants, OcflDigest, OcflExtension, OcflStorageLayout, OcflStore,
|
|
|
28
17
|
* @property {typeof extensions.NTupleOmitPrefixStorageLayout} NTupleOmitPrefixStorageLayout
|
|
29
18
|
* @property {typeof extensions.PathDirectStorageLayout} PathDirectStorageLayout
|
|
30
19
|
*/
|
|
31
|
-
|
|
20
|
+
|
|
21
|
+
/** @typedef {import('./lib/types').OcflExtensionConfig} OcflExtensionConfig */
|
|
22
|
+
/** @typedef {import('./lib/types').OcflObjectConfig} OcflObjectConfig */
|
|
23
|
+
/** @typedef {import('./lib/types').OcflStorageConfig} OcflStorageConfig */
|
|
32
24
|
|
|
33
25
|
/**
|
|
34
26
|
* Create a storage layout from extension name or config
|
|
@@ -46,106 +38,89 @@ function layout(config) {
|
|
|
46
38
|
}
|
|
47
39
|
let storageLayout = Object.assign(layout, /** @type {StorageLayout} */(OcflStorageLayout.layout))
|
|
48
40
|
|
|
49
|
-
|
|
50
41
|
/**
|
|
51
|
-
*
|
|
52
|
-
* Any backend store implementation module must return the
|
|
42
|
+
* Creates a new OCFL core API with a specific backend store.
|
|
43
|
+
* Any backend store implementation module must return the result of this method.
|
|
44
|
+
* @param {typeof OcflStore} store
|
|
45
|
+
* @param {SO} defaultOptions
|
|
53
46
|
* @template SO
|
|
47
|
+
* @returns Top level OCFL API interface that provides storage and object creation.
|
|
54
48
|
*/
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
49
|
+
function implementOcfl(store, defaultOptions) {
|
|
50
|
+
return {
|
|
51
|
+
OCFL_VERSIONS: OcflConstants.OCFL_VERSIONS,
|
|
52
|
+
|
|
53
|
+
/** Built-in storage layout classes */
|
|
54
|
+
StorageLayout: OcflStorageLayout.layout,
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Instantiate a storage layout from extension name or config
|
|
58
|
+
* @param {string | OcflExtensionConfig} config - Layout name or config
|
|
59
|
+
*/
|
|
60
|
+
storageLayout(config) {
|
|
61
|
+
let name, c;
|
|
62
|
+
if (typeof config === 'string') {
|
|
63
|
+
name = config;
|
|
64
|
+
} else {
|
|
65
|
+
name = config.extensionName;
|
|
66
|
+
c = config;
|
|
67
|
+
}
|
|
68
|
+
let cls = OcflStorageLayout.class(name)
|
|
69
|
+
if (!cls) throw new Error(`Layout "${name}" is not supported.`);
|
|
70
|
+
return cls.create(c);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Instantiate an OCFL Object
|
|
75
|
+
* @param {OcflObjectConfig} config
|
|
76
|
+
* @param {SO} [storeOptions]
|
|
77
|
+
*/
|
|
78
|
+
object(config, storeOptions = defaultOptions) {
|
|
79
|
+
return new OcflObject(config, store.getInstance(storeOptions));
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Instantiate an OCFL Storage root
|
|
84
|
+
* @param {OcflStorageConfig} config
|
|
85
|
+
* @param {SO} [storeOptions]
|
|
86
|
+
* @return {OcflStorage}
|
|
87
|
+
*/
|
|
88
|
+
storage(config, storeOptions = defaultOptions) {
|
|
89
|
+
return new OcflStorageImpl(config, store.getInstance(storeOptions));
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create a new OCFL Storage
|
|
94
|
+
* @param {OcflStorageConfig} config
|
|
95
|
+
* @param {SO} [storeOptions]
|
|
96
|
+
* @return {Promise<OcflStorage>}
|
|
97
|
+
*/
|
|
98
|
+
async createStorage(config, storeOptions = defaultOptions) {
|
|
99
|
+
return (new OcflStorageImpl(config, store.getInstance(storeOptions))).create();
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Load an existing OCFL Storage
|
|
104
|
+
* @param {OcflStorageConfig} config
|
|
105
|
+
* @param {SO} [storeOptions]
|
|
106
|
+
* @return {Promise<OcflStorage>}
|
|
107
|
+
*/
|
|
108
|
+
async loadStorage(config, storeOptions = defaultOptions) {
|
|
109
|
+
return (new OcflStorageImpl(config, store.getInstance(storeOptions))).load();
|
|
87
110
|
}
|
|
88
|
-
let cls = OcflStorageLayout.class(name)
|
|
89
|
-
if (!cls) throw new Error(`Layout "${name}" is not supported.`);
|
|
90
|
-
return cls.create(c);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Instantiate an OCFL Object object
|
|
95
|
-
* @param {OcflObjectConfig} config
|
|
96
|
-
* @param {SO} [storeOptions]
|
|
97
|
-
* @return {OcflObject}
|
|
98
|
-
*/
|
|
99
|
-
object(config, storeOptions = this.#defaultOptions) {
|
|
100
|
-
return new OcflObjectImpl(config, this.#store.getInstance(storeOptions));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Instantiate an OCFL Storage object
|
|
105
|
-
* @param {OcflStorageConfig} config
|
|
106
|
-
* @param {SO} [storeOptions]
|
|
107
|
-
* @return {OcflStorage}
|
|
108
|
-
*/
|
|
109
|
-
storage(config, storeOptions = this.#defaultOptions) {
|
|
110
|
-
return new OcflStorageImpl(config, this.#store.getInstance(storeOptions));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Create a new OCFL Storage
|
|
115
|
-
* @param {OcflStorageConfig} config
|
|
116
|
-
* @param {SO} [storeOptions]
|
|
117
|
-
* @return {Promise<OcflStorage>}
|
|
118
|
-
*/
|
|
119
|
-
async createStorage(config, storeOptions = this.#defaultOptions) {
|
|
120
|
-
return (new OcflStorageImpl(config, this.#store.getInstance(storeOptions))).create();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Load an existing OCFL Storage
|
|
125
|
-
* @param {OcflStorageConfig} config
|
|
126
|
-
* @param {SO} [storeOptions]
|
|
127
|
-
* @return {Promise<OcflStorage>}
|
|
128
|
-
*/
|
|
129
|
-
async loadStorage(config, storeOptions = this.#defaultOptions) {
|
|
130
|
-
return (new OcflStorageImpl(config, this.#store.getInstance(storeOptions))).load();
|
|
131
111
|
}
|
|
132
112
|
}
|
|
133
113
|
|
|
134
|
-
|
|
135
|
-
|
|
136
114
|
module.exports = {
|
|
137
115
|
OcflConstants,
|
|
138
116
|
OcflDigest,
|
|
139
117
|
OcflExtension, OcflStorageLayout,
|
|
140
118
|
OcflStore,
|
|
141
119
|
OcflObject,
|
|
142
|
-
OcflObjectImpl,
|
|
143
120
|
OcflObjectInventory,
|
|
144
121
|
OcflObjectTransactionImpl,
|
|
145
|
-
createObjectProxy,
|
|
146
|
-
createTransactionProxy,
|
|
147
122
|
OcflStorage, OcflStorageImpl,
|
|
148
|
-
|
|
123
|
+
implementOcfl,
|
|
149
124
|
extensions
|
|
150
125
|
};
|
|
151
126
|
|
package/index.mjs
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
//export * from './lib/constants.js';
|
|
7
7
|
|
|
8
8
|
export * from './index.js';
|
|
9
|
-
import ocfl from './index.js';
|
|
9
|
+
import * as ocfl from './index.js';
|
|
10
10
|
export default ocfl;
|
|
11
11
|
//export const { DIGEST, DIGEST_FIXITY, INVENTORY_NAME, NAMASTE_PREFIX_OBJECT, NAMASTE_PREFIX_STORAGE, OCFL_VERSION } = ocfl
|
|
12
|
-
// export const { OcflObject,
|
|
12
|
+
// export const { OcflObject, OcflObjectInventory, OcflObjectTransaction, OcflObjectTransactionImpl,
|
|
13
13
|
// OcflStorage, OcflStorageImpl,
|
|
14
|
-
// createProxy, digestFrom,
|
|
14
|
+
// createProxy, digestFrom, digestFromStream,
|
|
15
15
|
// DIGEST, DIGEST_FIXITY, INVENTORY_NAME, NAMASTE_PREFIX_OBJECT, NAMASTE_PREFIX_STORAGE, OCFL_VERSION } = ocfl;
|
|
16
16
|
//export const {} = ocfl;
|
|
17
17
|
//export { default as ocfl } from './index.js';
|
package/lib/digest.js
CHANGED
|
@@ -1,139 +1,307 @@
|
|
|
1
1
|
//@ts-check
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const fs = require("fs");
|
|
6
|
-
const stream = require("stream/promises");
|
|
3
|
+
const hashWasm = require('hash-wasm');
|
|
4
|
+
const { sha512_256 } = require('js-sha512'); // fallback for sha512/256 algorithm, which is not included in hash-wasm
|
|
7
5
|
//const worker = require('node:worker_threads');
|
|
8
|
-
const {
|
|
9
|
-
const {
|
|
6
|
+
const { enumeration } = require('./enum.js');
|
|
7
|
+
const { testSymbol } = require('./utils.js');
|
|
8
|
+
|
|
9
|
+
const CONTENT = enumeration(['sha256', 'sha512']);
|
|
10
|
+
const FIXITY = enumeration(['sha256', 'sha512', 'md5', 'sha1', 'blake2b-512', 'blake2b-160', 'blake2b-256', 'blake2b-384', 'sha512/256', 'size', 'crc32']);
|
|
10
11
|
|
|
11
12
|
/**
|
|
13
|
+
* @typedef {((outputType: "binary") => Uint8Array) & ((outputType?: "hex") => string)} IDigest
|
|
14
|
+
* @typedef {((outputType: "binary") => {[key:string]: Uint8Array}) & ((outputType?: "hex") => {[key:string]: string})} IMultiDigest
|
|
15
|
+
* @typedef {WritableStream & { update(data: IDataType): void, digest: IDigest }} WritableStreamHasher
|
|
16
|
+
* @typedef {WritableStream & { update(data: IDataType): void, digest: IMultiDigest }} WritableStreamMultiHasher
|
|
12
17
|
*/
|
|
13
|
-
class CONTENT extends Enum {
|
|
14
|
-
static sha256 = new this('sha256');
|
|
15
|
-
static sha512 = new this('sha512');
|
|
16
|
-
};
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
/**
|
|
20
|
+
* @type { Object.<string, CommonHasher[]> }
|
|
21
|
+
*/
|
|
22
|
+
const hasherCache = {};
|
|
23
|
+
/**
|
|
24
|
+
* @type { Object.<string, CommonHasher> }
|
|
25
|
+
*/
|
|
26
|
+
const hasherSyncCache = {};
|
|
27
|
+
// for (let algo of FIXITY) {
|
|
28
|
+
// algorithms[algo] = function () { return crypto.createHash(algo); }
|
|
29
|
+
// }
|
|
30
|
+
|
|
31
|
+
/** @type { WeakMap<CommonHasher, boolean>} */
|
|
32
|
+
const hasherPending = new WeakMap();
|
|
33
|
+
|
|
34
|
+
/** @type { {[key: string]: () => Promise<CommonHasher>} } */
|
|
35
|
+
const hasherFactory = {};
|
|
25
36
|
|
|
26
37
|
/**
|
|
27
|
-
*
|
|
38
|
+
* Register a hash algorithm and it's factory function.
|
|
39
|
+
* @param {string} name
|
|
40
|
+
* @param {() => Promise<CommonHasher>} factory
|
|
28
41
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.hash = hash;
|
|
42
|
+
function registerHasherFactory(name, factory) {
|
|
43
|
+
if (typeof factory === 'function') {
|
|
44
|
+
hasherFactory[name] = factory;
|
|
33
45
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function hasAlgorithm(name) {
|
|
49
|
+
return name in hasherFactory;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
(function init() {
|
|
53
|
+
for (const algo of FIXITY) {
|
|
54
|
+
if (algo.startsWith('blake2b')) {
|
|
55
|
+
const bits = parseInt(algo.split('-')[1]);
|
|
56
|
+
registerHasherFactory(algo, async () => hashWasm.createBLAKE2b(bits));
|
|
57
|
+
} else if (algo === 'sha512/256') {
|
|
58
|
+
registerHasherFactory(algo, async () => {
|
|
59
|
+
let h = sha512_256.create();
|
|
60
|
+
/** @type {CommonHasher} */
|
|
61
|
+
const hasher = {
|
|
62
|
+
digestSize: 32,
|
|
63
|
+
init() { h = sha512_256.create(); return this; },
|
|
64
|
+
update(data) { h.update(/** @type {any} */(data)); return this },
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
digest(encoding) {
|
|
67
|
+
if (encoding === 'binary') return new Uint8Array(h.digest());
|
|
68
|
+
else return h.hex();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
return hasher;
|
|
72
|
+
});
|
|
73
|
+
} else if (algo === 'size') {
|
|
74
|
+
registerHasherFactory(algo, async () => {
|
|
75
|
+
let encoder;
|
|
76
|
+
let size = 0;
|
|
77
|
+
/** @type {CommonHasher} */
|
|
78
|
+
const hasher = {
|
|
79
|
+
digestSize: 1,
|
|
80
|
+
init() {
|
|
81
|
+
size = 0;
|
|
82
|
+
encoder = new TextEncoder();
|
|
83
|
+
return this;
|
|
84
|
+
},
|
|
85
|
+
update(data) {
|
|
86
|
+
if (data) {
|
|
87
|
+
if (typeof data === 'string') size += encoder.encode(data).length;
|
|
88
|
+
else size += data.byteLength;
|
|
89
|
+
}
|
|
90
|
+
return this;
|
|
91
|
+
},
|
|
92
|
+
// @ts-ignore
|
|
93
|
+
digest(encoding) {
|
|
94
|
+
if (encoding === 'binary') return new Uint8Array(new BigUint64Array([BigInt(size)]).buffer);
|
|
95
|
+
else return '' + size;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
return hasher;
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
registerHasherFactory(algo, hashWasm['create' + algo.toUpperCase()]);
|
|
40
102
|
}
|
|
41
103
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
104
|
+
})();
|
|
105
|
+
// (async () => {
|
|
106
|
+
// for (const name in hasherFactory) {
|
|
107
|
+
// const factory = hasherFactory[name];
|
|
108
|
+
// const h = await factory();
|
|
109
|
+
// console.log(name);
|
|
110
|
+
// console.log(h);
|
|
111
|
+
// console.log(h.init().update('test').digest().length)
|
|
112
|
+
// }
|
|
113
|
+
// })();
|
|
46
114
|
|
|
47
115
|
/**
|
|
48
|
-
*
|
|
116
|
+
*
|
|
117
|
+
* @param {*} hasher
|
|
118
|
+
* @returns {hasher is MultiHasher}
|
|
49
119
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
algorithms[algo.value] = function () { return crypto.createHash(algo.name); }
|
|
120
|
+
function isMulti(hasher) {
|
|
121
|
+
return hasher.multi;
|
|
53
122
|
}
|
|
54
123
|
|
|
55
124
|
/**
|
|
56
|
-
*
|
|
125
|
+
* Create a hasher instance for the given algorithm or get it from cache if available.
|
|
57
126
|
* @param {string} algorithm
|
|
58
127
|
*/
|
|
59
|
-
function
|
|
60
|
-
const
|
|
61
|
-
|
|
128
|
+
async function createHasher(algorithm) {
|
|
129
|
+
const cacheArray = hasherCache[algorithm];
|
|
130
|
+
let hash = (cacheArray ?? []).find(hasher => !hasherPending.get(hasher));
|
|
131
|
+
if (!hash) {
|
|
132
|
+
const factory = hasherFactory[algorithm];
|
|
133
|
+
if (!factory) throw new Error(`Unsupported digest algorithm: ${algorithm}`);
|
|
134
|
+
hash = await factory();
|
|
135
|
+
if (cacheArray) cacheArray.push(hash);
|
|
136
|
+
else hasherCache[algorithm] = [hash];
|
|
137
|
+
}
|
|
138
|
+
hasherPending.set(hash, true);
|
|
139
|
+
hash.init();
|
|
62
140
|
return hash;
|
|
63
141
|
}
|
|
64
142
|
|
|
65
143
|
/**
|
|
66
|
-
*
|
|
67
|
-
* @param {string
|
|
68
|
-
* @param {object} [options]
|
|
144
|
+
* Create a hasher instance for calculating multiple types of digest in one go.
|
|
145
|
+
* @param {string|string[]} algorithms
|
|
69
146
|
*/
|
|
70
|
-
function
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
return
|
|
147
|
+
async function createMultiHasher(algorithms) {
|
|
148
|
+
const algoNames = Array.isArray(algorithms) ? (algorithms.length ? [...(new Set(algorithms))] : ['sha512']) : [algorithms];
|
|
149
|
+
const hashers = await Promise.all(algoNames.map(createHasher));
|
|
150
|
+
return /** @type { MultiHasher } */({
|
|
151
|
+
multi: true,
|
|
152
|
+
update(data) {
|
|
153
|
+
for (const hash of hashers) {
|
|
154
|
+
hash.update(data);
|
|
155
|
+
}
|
|
156
|
+
return this;
|
|
157
|
+
},
|
|
158
|
+
digest(encoding) {
|
|
159
|
+
const result = {};
|
|
160
|
+
for (let i = 0; i < hashers.length; i++) {
|
|
161
|
+
hasherPending.set(hashers[i], false);
|
|
162
|
+
result[algoNames[i]] = hashers[i].digest(encoding);
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
74
167
|
}
|
|
75
168
|
|
|
76
169
|
/**
|
|
77
|
-
*
|
|
78
|
-
* @
|
|
79
|
-
* @param {
|
|
80
|
-
* @return {
|
|
170
|
+
* Create a hash instance that implements WritableStream.
|
|
171
|
+
* @overload
|
|
172
|
+
* @param {string} algorithm One or more digest algorithm names
|
|
173
|
+
* @return {Promise<WritableStreamHasher>}
|
|
81
174
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
175
|
+
/**
|
|
176
|
+
* @overload
|
|
177
|
+
* @param {string[]} algorithm One or more digest algorithm names
|
|
178
|
+
* @return {Promise<WritableStreamMultiHasher>}
|
|
179
|
+
*/
|
|
180
|
+
async function createStream(algorithm = 'sha512') {
|
|
181
|
+
const hash = await (Array.isArray(algorithm) ? createMultiHasher(algorithm) : createHasher(algorithm));
|
|
182
|
+
/** @type {any} */
|
|
183
|
+
const ws = new WritableStream({
|
|
184
|
+
write(chunk) {
|
|
185
|
+
hash.update(chunk);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
ws.update = function (data) {
|
|
189
|
+
hash.update(data);
|
|
190
|
+
};
|
|
191
|
+
ws.digest = function (encoding) {
|
|
192
|
+
if (!isMulti(hash)) hasherPending.set(hash, false);
|
|
193
|
+
return hash.digest(encoding);
|
|
87
194
|
}
|
|
88
|
-
return
|
|
195
|
+
return ws;
|
|
89
196
|
}
|
|
90
197
|
|
|
198
|
+
/** @typedef {TransformStream & { digest: IMultiDigest }} StreamThroughHash */
|
|
91
199
|
/**
|
|
92
|
-
*
|
|
93
|
-
* @param {string} algorithm
|
|
94
|
-
* @param {
|
|
200
|
+
* Create a pass through stream that pass data as it is and calculate the digest as data pass through.
|
|
201
|
+
* @param {string|string[]} [algorithm]
|
|
202
|
+
* @param {object} [options]
|
|
203
|
+
* @return {Promise<StreamThroughHash>}
|
|
95
204
|
*/
|
|
96
|
-
async function
|
|
97
|
-
|
|
205
|
+
async function createStreamThrough(algorithm = 'sha512', options) {
|
|
206
|
+
const hash = await (Array.isArray(algorithm) ? createMultiHasher(algorithm) : createHasher(algorithm));
|
|
207
|
+
const { writableStrategy, readableStrategy } = options || {};
|
|
208
|
+
//hash.setEncoding('hex');
|
|
209
|
+
/** @type {any} */
|
|
210
|
+
const ts = new TransformStream({
|
|
211
|
+
transform(chunk, controller) {
|
|
212
|
+
controller.enqueue(chunk);
|
|
213
|
+
hash.update(chunk);
|
|
214
|
+
}
|
|
215
|
+
}, writableStrategy, readableStrategy);
|
|
216
|
+
ts.digest = function (encoding) {
|
|
217
|
+
if (!isMulti(hash)) hasherPending.set(hash, false);
|
|
218
|
+
return hash.digest(encoding);
|
|
219
|
+
}
|
|
220
|
+
return ts;
|
|
98
221
|
}
|
|
99
222
|
|
|
100
223
|
/**
|
|
101
224
|
*
|
|
225
|
+
* @overload
|
|
226
|
+
* @param {string[]} algorithm
|
|
227
|
+
* @param {IDataType | ReadableStream} input
|
|
228
|
+
* @return {Promise<{[key:string]: string}>}
|
|
229
|
+
*/
|
|
230
|
+
/**
|
|
231
|
+
* @overload
|
|
102
232
|
* @param {string} algorithm
|
|
103
|
-
* @param {
|
|
233
|
+
* @param {IDataType | ReadableStream} input
|
|
234
|
+
* @return {Promise<string>}
|
|
104
235
|
*/
|
|
105
|
-
async function
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
236
|
+
async function digest(algorithm, input) {
|
|
237
|
+
if (input instanceof ReadableStream) {
|
|
238
|
+
const hash = await createStream(algorithm);
|
|
239
|
+
await input.pipeTo(hash);
|
|
240
|
+
return hash.digest();
|
|
241
|
+
} else {
|
|
242
|
+
const hash = await (Array.isArray(algorithm) ? createMultiHasher(algorithm) : createHasher(algorithm));
|
|
243
|
+
if (!isMulti(hash)) hasherPending.set(hash, false);
|
|
244
|
+
return hash.update(input).digest();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function initSync(algorithm) {
|
|
249
|
+
if (!hasherSyncCache[algorithm]) {
|
|
250
|
+
const factory = hasherFactory[algorithm];
|
|
251
|
+
if (!factory) throw new Error(`Unsupported digest algorithm: ${algorithm}`);
|
|
252
|
+
const hasher = await factory();
|
|
253
|
+
hasherSyncCache[algorithm] = hasher;
|
|
254
|
+
}
|
|
109
255
|
}
|
|
110
256
|
|
|
111
257
|
/**
|
|
112
|
-
*
|
|
258
|
+
* For this sync function to work correctly, it requires a call to initSync() to be awaited for each algorithm before calling this function.
|
|
113
259
|
* @param {string} algorithm
|
|
114
|
-
* @param {
|
|
260
|
+
* @param {IDataType} input
|
|
115
261
|
*/
|
|
116
|
-
|
|
117
|
-
|
|
262
|
+
function digestSync(algorithm, input) {
|
|
263
|
+
const hasher = hasherSyncCache[algorithm];
|
|
264
|
+
if (!hasher) throw new Error(`Hasher for algorithm ${algorithm} is not initialized. Please call and await initSync('${algorithm}') before calling this function.`);
|
|
265
|
+
return hasher.init().update(input).digest();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const HEX_DIGEST_LENGTH = {
|
|
269
|
+
'md5': 32,
|
|
270
|
+
'sha1': 40,
|
|
271
|
+
'sha256': 64,
|
|
272
|
+
'sha512/256': 64,
|
|
273
|
+
'sha512': 128,
|
|
274
|
+
'blake2b-160': 40,
|
|
275
|
+
'blake2b-256': 64,
|
|
276
|
+
'blake2b-384': 96,
|
|
277
|
+
'blake2b-512': 128,
|
|
278
|
+
'crc32': 8,
|
|
279
|
+
'size': 1
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
function getHexDigestLength(algorithm) {
|
|
283
|
+
return HEX_DIGEST_LENGTH[algorithm] || 0;
|
|
118
284
|
}
|
|
119
285
|
|
|
120
286
|
const OcflDigest = {
|
|
121
287
|
CONTENT,
|
|
122
288
|
FIXITY,
|
|
123
|
-
algorithms,
|
|
124
|
-
createStream, createStreamThrough,
|
|
125
|
-
|
|
126
|
-
|
|
289
|
+
// algorithms,
|
|
290
|
+
createStream, createStreamThrough, digest,
|
|
291
|
+
registerHasherFactory, hasAlgorithm, getHexDigestLength,
|
|
292
|
+
digestSync, initSync
|
|
127
293
|
};
|
|
128
294
|
|
|
129
295
|
module.exports = {
|
|
130
|
-
OcflDigest
|
|
296
|
+
OcflDigest,
|
|
297
|
+
[testSymbol]: {
|
|
298
|
+
hasherCache
|
|
299
|
+
}
|
|
131
300
|
// DIGEST,
|
|
132
301
|
// DIGEST_FIXITY,
|
|
133
302
|
// digestSync,
|
|
134
303
|
// digestAsync,
|
|
135
304
|
// digestFromStream,
|
|
136
|
-
// digestFromFile,
|
|
137
305
|
// digestStream,
|
|
138
306
|
// HashThrough
|
|
139
307
|
};
|