@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 +16 -0
- package/index.js +131 -0
- package/index.mjs +18 -0
- package/lib/constants.js +24 -0
- package/lib/digest.js +138 -0
- package/lib/enum.js +92 -0
- package/lib/error.js +19 -0
- package/lib/extension.js +206 -0
- package/lib/extensions/0001-digest-algorithms.js +41 -0
- package/lib/extensions/0002-flat-direct-storage-layout.js +46 -0
- package/lib/extensions/0003-hash-and-id-n-tuple-storage-layout.js +58 -0
- package/lib/extensions/0004-hashed-n-tuple-storage-layout.js +61 -0
- package/lib/extensions/0005-mutable-head.js +0 -0
- package/lib/extensions/0006-flat-omit-prefix-storage-layout.js +7 -0
- package/lib/extensions/0007-n-tuple-omit-prefix-storage-layout.js +7 -0
- package/lib/extensions/0008-schema-registry.js +0 -0
- package/lib/extensions/index.js +12 -0
- package/lib/index.js +25 -0
- package/lib/inventory.js +393 -0
- package/lib/object.js +530 -0
- package/lib/storage.js +324 -0
- package/lib/store.js +153 -0
- package/lib/temp.js +264 -0
- package/lib/transaction.js +413 -0
- package/lib/types.d.ts +44 -0
- package/lib/utils.js +92 -0
- package/lib/validation.js +174 -0
- package/package.json +63 -0
- package/resources/ocfl_1.0.txt +936 -0
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';
|
package/lib/constants.js
ADDED
|
@@ -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
|
+
};
|
package/lib/extension.js
ADDED
|
@@ -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 };
|