@ocfl/ocfl 0.1.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/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # ocfl-js - Common modules
2
+ This is JavaScript/Node.js library to create and interact with Oxford Common File Layout (OCFL) storage and objects within it.
3
+ This package contains abstract classes definition. Do not use this package directly. To work with an actual OCFL storage and object, use the following concrete implementation:
4
+ * ocfl-fs - File system storage backend
5
+
6
+ ## Extensions
7
+ This library by default includes all the community extensions defined in the [OCFL Community Extensions page](https://github.com/OCFL/extensions). The implementation is organised in the ocfl-extensions package.
8
+
9
+ To implement a local extension in this library, first follow the description in that page, then create a function like in the following example:
10
+
11
+ const ocfl = require(ocfl-fs);
12
+ ocfl.extensions['9999-local-test'] = function (Storage, Object) {
13
+
14
+
15
+ }
16
+
package/index.js ADDED
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @module ocfl
3
+ */
4
+
5
+ const { OcflConstants, OcflDigest, OcflExtension, OcflStorageLayout, OcflStore,
6
+ OcflObject, OcflObjectImpl,
7
+ OcflObjectInventory, OcflObjectTransactionImpl,
8
+ OcflStorage, OcflStorageImpl,
9
+ createObjectProxy, createTransactionProxy,
10
+ extensions
11
+ } = require('./lib/index.js');
12
+
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
+ /**
23
+ * @typedef {Object} StorageLayout
24
+ * @property {typeof extensions.FlatDirectStorageLayout} [FlatDirectStorageLayout]
25
+ * @property {typeof extensions.FlatOmitPrefixStorageLayout} [FlatOmitPrefixStorageLayout]
26
+ * @property {typeof extensions.HashAndIdNTupleStorageLayout} [HashAndIdNTupleStorageLayout]
27
+ * @property {typeof extensions.HashedNTupleStorageLayout} [HashedNTupleStorageLayout]
28
+ * @property {typeof extensions.NTupleOmitPrefixStorageLayout} [NTupleOmitPrefixStorageLayout]
29
+ */
30
+ /** @typedef {import('./lib/extension.js').OcflExtensionConfig} OcflExtensionConfig */
31
+
32
+ /**
33
+ * Create a storage layout from extension name or config
34
+ * @param {string|OcflExtensionConfig} config - Layout name or config
35
+ */
36
+ function layout(config) {
37
+ let name, c;
38
+ if (typeof config === 'string') {
39
+ name = config;
40
+ } else {
41
+ name = config.extensionName;
42
+ c = config;
43
+ }
44
+ return OcflStorageLayout.class(name).create(c);
45
+ }
46
+ let storageLayout = Object.assign(layout, /** @type {StorageLayout} */(OcflStorageLayout.layout))
47
+
48
+
49
+ /**
50
+ * Top level OCFL API interface that provides storage and object creation.
51
+ * Any backend store implementation module must return the instance of this class.
52
+ * @template SO
53
+ */
54
+ class Ocfl {
55
+ #store;
56
+ #defaultOptions;
57
+
58
+ /**
59
+ * @param {typeof OcflStore} store
60
+ * @param {SO} defaultOptions
61
+ */
62
+ constructor(store, defaultOptions) {
63
+ this.#store = store;
64
+ this.#defaultOptions = defaultOptions;
65
+ }
66
+
67
+ get OCFL_VERSIONS() { return OcflConstants.OCFL_VERSIONS; }
68
+
69
+ /**
70
+ * Built-in storage layout classes
71
+ * @return {StorageLayout}
72
+ */
73
+ get StorageLayout() { return OcflStorageLayout.layout }
74
+
75
+ /**
76
+ * Create a storage layout from extension name or config
77
+ * @param {string|OcflExtensionConfig} config - Layout name or config
78
+ */
79
+ storageLayout(config) {
80
+ let name, c;
81
+ if (typeof config === 'string') {
82
+ name = config;
83
+ } else {
84
+ name = config.extensionName;
85
+ c = config;
86
+ }
87
+ let cls = OcflStorageLayout.class(name)
88
+ if (!cls) throw new Error(`Layout "${name}" is not supported.`);
89
+ return cls.create(c);
90
+ }
91
+
92
+ /**
93
+ * Create an OCFL Object
94
+ * @param {OcflObjectConfig} config
95
+ * @param {SO} [storeOptions]
96
+ * @return {OcflObject}
97
+ */
98
+ object(config, storeOptions = this.#defaultOptions) {
99
+ return new OcflObjectImpl(config, this.#store.getInstance(storeOptions));
100
+ }
101
+
102
+ /**
103
+ * Create an OCFL Storage
104
+ * @param {OcflStorageConfig} config
105
+ * @param {SO} [storeOptions]
106
+ * @return {OcflStorage}
107
+ */
108
+ storage(config, storeOptions = this.#defaultOptions) {
109
+ return new OcflStorageImpl(config, this.#store.getInstance(storeOptions));
110
+ }
111
+ }
112
+
113
+ module.exports = {
114
+ OcflConstants,
115
+ OcflDigest,
116
+ OcflExtension, OcflStorageLayout,
117
+ OcflStore,
118
+ OcflObject,
119
+ OcflObjectImpl,
120
+ OcflObjectInventory,
121
+ OcflObjectTransactionImpl,
122
+ createObjectProxy,
123
+ createTransactionProxy,
124
+ OcflStorage, OcflStorageImpl,
125
+ Ocfl,
126
+ extensions
127
+ };
128
+
129
+ for (let name in extensions) {
130
+ extensions[name].register();
131
+ }
package/index.mjs ADDED
@@ -0,0 +1,18 @@
1
+ //@ts-check
2
+ // export * from './lib/storage.js';
3
+ // export * from './lib/object.js';
4
+ // export * from './lib/digest.js';
5
+ //export * from './lib/extension.js';
6
+ //export * from './lib/constants.js';
7
+
8
+ export * from './index.js';
9
+ import ocfl from './index.js';
10
+ export default ocfl;
11
+ //export const { DIGEST, DIGEST_FIXITY, INVENTORY_NAME, NAMASTE_PREFIX_OBJECT, NAMASTE_PREFIX_STORAGE, OCFL_VERSION } = ocfl
12
+ // export const { OcflObject, OcflObjectImpl, OcflObjectInventory, OcflObjectTransaction, OcflObjectTransactionImpl,
13
+ // OcflStorage, OcflStorageImpl,
14
+ // createProxy, digestFrom, digestFromFile, digestFromStream,
15
+ // DIGEST, DIGEST_FIXITY, INVENTORY_NAME, NAMASTE_PREFIX_OBJECT, NAMASTE_PREFIX_STORAGE, OCFL_VERSION } = ocfl;
16
+ //export const {} = ocfl;
17
+ //export { default as ocfl } from './index.js';
18
+ //export {Storage, Object} from './index.js';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @module
3
+ * default constants
4
+ */
5
+ // module.exports = {
6
+ // OCFL_VERSION: 1.1,
7
+ // NAMASTE_PREFIX_OBJECT: '0=ocfl_object_',
8
+ // NAMASTE_PREFIX_STORAGE: '0=ocfl_',
9
+ // INVENTORY_NAME: 'inventory.json'
10
+ // };
11
+ const OcflConstants = {
12
+ OCFL_VERSION: '1.1',
13
+ OCFL_VERSIONS: Object.freeze(['1.1', '1.0']),
14
+ OCFL_LAYOUT: 'ocfl_layout.json',
15
+ NAMASTE_T: '0=',
16
+ NAMASTE_PREFIX_OBJECT: 'ocfl_object_',
17
+ NAMASTE_PREFIX_STORAGE: 'ocfl_',
18
+ INVENTORY_NAME: 'inventory.json',
19
+ EXTENSIONS_DIR: 'extensions',
20
+ EXTENSION_CONFIG: 'config.json',
21
+ DIGEST_ALGORITHM: 'sha512'
22
+ };
23
+
24
+ module.exports = { OcflConstants };
package/lib/digest.js ADDED
@@ -0,0 +1,138 @@
1
+ //@ts-check
2
+
3
+ //const hasha = require("hasha");
4
+ const crypto = require("crypto");
5
+ const fs = require("fs");
6
+ const stream = require("stream/promises");
7
+ //const worker = require('node:worker_threads');
8
+ const { Enum } = require('./enum.js');
9
+ const { Transform } = require('stream');
10
+
11
+ /**
12
+ */
13
+ class CONTENT extends Enum {
14
+ static sha256 = new this('sha256');
15
+ static sha512 = new this('sha512');
16
+ };
17
+
18
+ class FIXITY extends Enum {
19
+ static sha256 = new this('sha256');
20
+ static sha512 = new this('sha512');
21
+ static md5 = new this('md5');
22
+ static sha1 = new this('sha1');
23
+ static blake2b512 = new this('blake2b-512');
24
+ };
25
+
26
+ /**
27
+ * A pass through stream that pass data as it is and calculate the digest as data pass through.
28
+ */
29
+ class HashThrough extends Transform {
30
+ constructor(hash, streamOptions) {
31
+ super(streamOptions);
32
+ this.hash = hash;
33
+ }
34
+ _transform(chunk, encoding, cb) {
35
+ try {
36
+ this.hash.update(chunk, encoding);
37
+ cb(null, chunk);
38
+ } catch (err) {
39
+ cb(err)
40
+ }
41
+ }
42
+ digest(encoding) {
43
+ return this.hash.digest(encoding || 'hex');
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @type { Object.<string, function():crypto.Hash>}
49
+ */
50
+ let algorithms = {};
51
+ for (let algo of FIXITY) {
52
+ algorithms[algo.value] = function () { return crypto.createHash(algo.name); }
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param {string} algorithm
58
+ */
59
+ function createStream(algorithm) {
60
+ const hash = algorithms[algorithm || 'sha512']();
61
+ hash.setEncoding('hex');
62
+ return hash;
63
+ }
64
+
65
+ /**
66
+ *
67
+ * @param {string} algorithm
68
+ */
69
+ function createStreamThrough(algorithm, options) {
70
+ const hash = algorithms[algorithm || 'sha512']();
71
+ hash.setEncoding('hex');
72
+ return new HashThrough(hash, options);
73
+ }
74
+
75
+ /**
76
+ *
77
+ * @param {string} algorithm
78
+ * @param {Buffer | string | Array<Buffer | string>} input
79
+ * @return {string}
80
+ */
81
+ function digestSync(algorithm, input) {
82
+ const hash = algorithms[algorithm || 'sha512']();
83
+ let inputs = Array.isArray(input) ? input : [input];
84
+ for (let chunk of inputs) {
85
+ hash.update(chunk);
86
+ }
87
+ return hash.digest('hex');
88
+ }
89
+
90
+ /**
91
+ *
92
+ * @param {string} algorithm
93
+ * @param {Buffer | string | Array<Buffer | string>} input
94
+ */
95
+ async function digestAsync(algorithm, input) {
96
+ return digestSync(algorithm, input);
97
+ }
98
+
99
+ /**
100
+ *
101
+ * @param {string} algorithm
102
+ * @param {NodeJS.ReadableStream} input
103
+ */
104
+ async function digestFromStream(algorithm, input) {
105
+ let hash = algorithms[algorithm]();
106
+ await stream.pipeline(input, hash);
107
+ return hash.digest('hex');
108
+ }
109
+
110
+ /**
111
+ *
112
+ * @param {string} algorithm
113
+ * @param {string} filePath
114
+ */
115
+ async function digestFromFile(algorithm, filePath) {
116
+ return await digestFromStream(algorithm, fs.createReadStream(filePath));
117
+ }
118
+
119
+ const OcflDigest = {
120
+ CONTENT,
121
+ FIXITY,
122
+ algorithms,
123
+ createStream, createStreamThrough,
124
+ digestAsync, digestSync,
125
+ digestFromStream, digestFromFile
126
+ };
127
+
128
+ module.exports = {
129
+ OcflDigest
130
+ // DIGEST,
131
+ // DIGEST_FIXITY,
132
+ // digestSync,
133
+ // digestAsync,
134
+ // digestFromStream,
135
+ // digestFromFile,
136
+ // digestStream,
137
+ // HashThrough
138
+ };
package/lib/enum.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Enumeration tool for working with a set of options
3
+ * @module enum
4
+ */
5
+
6
+ /**
7
+ * Create enumeration type by extending the Enum class and
8
+ * defining the name and value of each constant as static fields.
9
+ * @template V
10
+ */
11
+ class Enum {
12
+ /** @type {string} */
13
+ #name;
14
+ /** @type {V} */
15
+ #value;
16
+
17
+ /**
18
+ * Return an existing constant that represents the specified value
19
+ * @type {<T extends typeof Enum>(this: T, value: any) => InstanceType<T>}
20
+ */
21
+ static of = function (value, c=this) {
22
+ //if (value instanceof c) return value;
23
+ for (let name in c) {
24
+ if (c[name].value === value) return c[name];
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Check if the Enum type contains an Enum instance
30
+ * @param {Enum} instance
31
+ */
32
+ static has(instance) {
33
+ for (let name in this) {
34
+ if (this[name] === instance) return true;
35
+ }
36
+ return false;
37
+ }
38
+
39
+ static ofOrdinal(index, c=this) {
40
+ return c['_enums'][index];
41
+ }
42
+
43
+ static freeze(c=this) {
44
+ return Object.freeze(c);
45
+ }
46
+
47
+ static size(c=this) {
48
+ return c['_enums'].length;
49
+ }
50
+
51
+ static [Symbol.iterator]() {
52
+ return this['_enums'][Symbol.iterator]();
53
+ }
54
+
55
+ /**
56
+ * @param {V} [value]
57
+ */
58
+ constructor(value) {
59
+ let c = this.constructor;
60
+ if (!Object.hasOwn(c, '_enums')) {
61
+ let p = Object.getPrototypeOf(c);
62
+ Object.defineProperty(c, '_enums', { value: p['_enums']?.slice(0) ?? [] });
63
+ }
64
+ this.#value = value;
65
+ this.ordinal = c['_enums'].length;
66
+ c['_enums'].push(this);
67
+ Object.freeze(this);
68
+ }
69
+
70
+ get name() {
71
+ if (!this.#name) {
72
+ let ec = this.constructor;
73
+ for (let key in ec) {
74
+ if (ec[key] === this) {
75
+ this.#name = key;
76
+ break;
77
+ }
78
+ }
79
+ }
80
+ return this.#name;
81
+ }
82
+
83
+ get value() {
84
+ return this.#value ?? this.name;
85
+ }
86
+
87
+ toString() {
88
+ return this.value?.toString();
89
+ }
90
+
91
+ }
92
+ module.exports = { Enum };
package/lib/error.js ADDED
@@ -0,0 +1,19 @@
1
+ class ValidationError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'ValidationError';
5
+ }
6
+ }
7
+
8
+ class NotImplementedError extends Error {
9
+ constructor(methodName) {
10
+ let msg = methodName ? 'Method ' + methodName + ' has not been implemented' : '';
11
+ super(msg);
12
+ this.name = 'NotImplementedError';
13
+ }
14
+ }
15
+
16
+ module.exports = {
17
+ ValidationError,
18
+ NotImplementedError
19
+ };
@@ -0,0 +1,206 @@
1
+ //const {} = require('./object');
2
+ //const digest = require('./digest');
3
+
4
+ const { NotImplementedError } = require("./error");
5
+
6
+ /** Index by Registered extension name */
7
+ const extensionByName = {};
8
+ /** Index by extension class name */
9
+ const extensionByClassName = {};
10
+ /** @type {Object.<string, typeof OcflStorageLayout>} */
11
+ const storageLayout = {};
12
+
13
+ /** @typedef {{ extensionName?: string;[key: string]: any; }} OcflExtensionConfig */
14
+
15
+ // class OcflExtensionConfig {
16
+ // /** User defined config */
17
+ // definedConfig;
18
+ // constructor(config) {
19
+ // this.definedConfig = config;
20
+ // }
21
+
22
+ // }
23
+
24
+ /**
25
+ * @template {{}} C
26
+ */
27
+ class OcflExtension {
28
+ static #object = {};
29
+ static #storage = {};
30
+
31
+ /**
32
+ * Get an object-level extension
33
+ * @param {string} name - A registered extension name
34
+ */
35
+ static object(name) {
36
+ return this.#object[name];
37
+ }
38
+ /**
39
+ * Get an storage-level extension
40
+ * @param {string} name - A registered extension name
41
+ */
42
+ static storage(name) {
43
+ return this.#storage[name];
44
+ }
45
+
46
+ /** List all registered extension classes */
47
+ static get classes() {
48
+ return Object.values(extensionByName);
49
+ }
50
+
51
+ /**
52
+ * Find a registered extension with the specified name
53
+ * @param {string} name - extension class name or registered extension name
54
+ * @return {typeof OcflExtension} - the extension class
55
+ */
56
+ static class(name) {
57
+ return extensionByName[name] || extensionByClassName[name];
58
+ }
59
+
60
+ /**
61
+ * Register an extension
62
+ * @param {typeof OcflExtension} extensionClass
63
+ */
64
+ static register(extensionClass = this) {
65
+ let name = extensionClass.NAME;
66
+ extensionByName[name] = extensionClass;
67
+ extensionByClassName[extensionClass.name] = extensionClass;
68
+ // if (extensionClass.forObject) {
69
+ // if (this.#object[name]) throw new Error(`Extension ${name} already registered.`);
70
+ // this.#object[name] = extensionClass;
71
+ // }
72
+ // if (extensionClass.forStorage) {
73
+ // if (this.#storage[name]) throw new Error(`Extension ${name} already registered.`);
74
+ // this.#storage[name] = extensionClass;
75
+ // }
76
+ extensionClass.setup(require('./index.js'));
77
+ }
78
+
79
+ /**
80
+ * @template {typeof OcflExtension} T
81
+ * @param {OcflExtensionConfig} [config]
82
+ * @this {T}
83
+ * @return {InstanceType<T>}
84
+ */
85
+ static create2(config){
86
+ if (!config?.extensionName || this.NAME === config.extensionName) return /**@type {InstanceType<T>}*/(new this(config));
87
+ return OcflExtension.class(config.extensionName).create2(config);
88
+ }
89
+ /**
90
+ * Create an instance of the extension
91
+ * @type {<T extends typeof OcflExtension>(this: T, config?: OcflExtensionConfig) => InstanceType<T>}
92
+ */
93
+ static create = function (config) {
94
+ // @ts-ignore
95
+ if (!config?.extensionName || this.NAME === config.extensionName) return new this(config);
96
+ // @ts-ignore
97
+ return OcflExtension.class(config.extensionName).create(config);
98
+ }
99
+
100
+ static get forObject() { return false; }
101
+ static get forStorage() { return false; }
102
+ static setup(ocfl) { };
103
+ static get NAME() { return '0000-example-extension' }
104
+ static get DESCRIPTION() { return 'Example extension' }
105
+
106
+ /**
107
+ *
108
+ * @param {OcflExtensionConfig} [config]
109
+ * @param {C} [defaultConfig]
110
+ */
111
+ constructor(config, defaultConfig) {
112
+ /** @type {C} */
113
+ this.parameters = defaultConfig ? Object.create(/**@type{object}*/(defaultConfig)) : {};
114
+ if (typeof config === 'object') {
115
+ for (let k in config) {
116
+ if (k in defaultConfig) this.parameters[k] = config[k];
117
+ }
118
+ }
119
+ }
120
+
121
+ /** Return all the parameters in which the value has been set to non-default value */
122
+ get config() {
123
+ let paramNames = Object.keys(this.parameters);
124
+ if (paramNames.length > 0) {
125
+ return paramNames.reduce( (params, name) => (params[name] = this.parameters[name], params), {extensionName: this.name});
126
+ }
127
+ }
128
+
129
+ get p() { return this.parameters; }
130
+
131
+ /** Extension registered name */
132
+ get name() { return /** @type {typeof OcflExtension}*/(this.constructor).NAME }
133
+ get description() { return /** @type {typeof OcflExtension}*/(this.constructor).DESCRIPTION }
134
+ get minOcflVersion() { return "1.0" }
135
+ get version() { return "1.0" }
136
+
137
+ /**
138
+ * Get the specified parameter value
139
+ * @param {string} param - The extension parameter name
140
+ * @return {*}
141
+ */
142
+ get(param) { return this.parameters[param]; }
143
+
144
+ /**
145
+ * Set the specified parameter value
146
+ * @param {string} param - The extension parameter name
147
+ * @param {*} value - The extension parameter value
148
+ */
149
+ set(param, value) { if (param in this.parameters) this.parameters[param] = value; }
150
+ }
151
+
152
+
153
+ /**
154
+ * @template {{}} C
155
+ * @extends {OcflExtension<C>}
156
+ */
157
+ class OcflStorageLayout extends OcflExtension {
158
+
159
+ static get layout() {
160
+ return storageLayout;
161
+ }
162
+
163
+ /**
164
+ *
165
+ * @param {string} name
166
+ * @return {typeof OcflStorageLayout}
167
+ */
168
+ static class(name) {
169
+ let c = super.class(name);
170
+ if (c && c.prototype instanceof OcflStorageLayout) return /** @type {typeof OcflStorageLayout} */(c);
171
+ }
172
+
173
+ static get classes() {
174
+ return Object.values(storageLayout);
175
+ }
176
+
177
+ /**
178
+ * Register a storage layout extension
179
+ * @param {typeof OcflStorageLayout} extensionClass
180
+ */
181
+ static register(extensionClass = this) {
182
+ super.register(extensionClass);
183
+ storageLayout[extensionClass.name] = extensionClass;
184
+ }
185
+
186
+ static get forStorage() { return true; }
187
+
188
+ /**
189
+ *
190
+ * @param {OcflExtensionConfig} [config]
191
+ * @param {C} [defaultConfig]
192
+ */
193
+ constructor(config, defaultConfig) { super(config, defaultConfig); }
194
+
195
+ /**
196
+ * Map an object identifier to a path
197
+ * @param {string} id - The identifier of the OCFL Object
198
+ * @return {string}
199
+ */
200
+ map(id) { throw new NotImplementedError(); }
201
+ }
202
+
203
+ module.exports = {
204
+ OcflExtension,
205
+ OcflStorageLayout
206
+ };
@@ -0,0 +1,41 @@
1
+ //@ts-check
2
+
3
+ const {OcflExtension} = require("../extension");
4
+ const crypto = require('crypto');
5
+ const blake2 = require('blake2');
6
+
7
+ class DigestAlgorithm extends OcflExtension {
8
+ static get NAME() { return '0001-digest-algorithms' }
9
+
10
+ // static create() {
11
+ // if (!instance) instance = new DigestAlgorithm();
12
+ // return instance;
13
+ // }
14
+ /**
15
+ *
16
+ * @param {import('ocfl')} ocfl
17
+ */
18
+ static setup(ocfl) {
19
+ let {OcflDigest} = ocfl;
20
+ let { FIXITY } = OcflDigest;
21
+ class FIXITY_EX extends FIXITY {
22
+ static blake2b160 = new this('blake2b-160');
23
+ static blake2b256 = new this('blake2b-256');
24
+ static blake2b384 = new this('blake2b-384');
25
+ static ['sha512-256'] = new this('sha512/256');
26
+ };
27
+ OcflDigest.FIXITY = FIXITY_EX;
28
+ //console.log(OcflDigest.FIXITY);
29
+ for (let len of [160, 256, 384]) {
30
+ OcflDigest.algorithms['blake2b-' + len] = function() {
31
+ return blake2.createHash('blake2b', {digestLength: len/8});
32
+ }
33
+ }
34
+ OcflDigest.algorithms['sha512/256'] = function() {
35
+ return crypto.createHash('sha512-256');
36
+ }
37
+ //console.log(OcflDigest.algorithms);
38
+ }
39
+ }
40
+
41
+ module.exports = { DigestAlgorithm };