@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/lib/storage.js
CHANGED
|
@@ -5,21 +5,23 @@ const validation = require('./validation.js');
|
|
|
5
5
|
const { NotImplementedError } = require("./error");
|
|
6
6
|
const { OcflStorageLayout } = require("./extension");
|
|
7
7
|
const { HashedNTupleStorageLayout } = require("./extensions/0004-hashed-n-tuple-storage-layout");
|
|
8
|
-
const { OcflObject
|
|
8
|
+
const { OcflObject } = require("./object");
|
|
9
9
|
const { isDirEmpty, findNamasteVersion } = require("./utils.js");
|
|
10
10
|
const { OCFL_VERSION, OCFL_VERSIONS, OCFL_LAYOUT,
|
|
11
11
|
EXTENSIONS_DIR, EXTENSION_CONFIG,
|
|
12
12
|
NAMASTE_PREFIX_STORAGE, NAMASTE_PREFIX_OBJECT, NAMASTE_T } = require('./constants').OcflConstants;
|
|
13
|
-
const
|
|
14
|
-
|
|
13
|
+
const { OcflDigest } = require('./digest');
|
|
15
14
|
|
|
16
15
|
const DEFAULT_LAYOUT = HashedNTupleStorageLayout;
|
|
17
16
|
|
|
17
|
+
/** @typedef {import('./store').OcflStore} OcflStore */
|
|
18
|
+
/** @typedef {import('./types.js').OcflExtensionConfig} OcflExtensionConfig */
|
|
19
|
+
/** @typedef {import('./types.js').OcflVersion} OcflVersion */
|
|
20
|
+
|
|
21
|
+
|
|
18
22
|
/**
|
|
19
23
|
* An abstraction to represent the OCFL Storage Root, which is
|
|
20
24
|
* a base directory used to store OCFL Objects, identified by a [NAMASTE] file “0=ocfl_1.1”.
|
|
21
|
-
* @typedef {import('./extension').OcflExtensionConfig} OcflExtensionConfig
|
|
22
|
-
* @typedef {import('./store').OcflStore} OcflStore
|
|
23
25
|
*/
|
|
24
26
|
|
|
25
27
|
class OcflStorage {
|
|
@@ -122,13 +124,13 @@ class OcflStorage {
|
|
|
122
124
|
* @property {('sha256' | 'sha512')} [digestAlgorithm] - Digest algorithm for content-addressing, must use either sha512 or sha256
|
|
123
125
|
* @property {string} [contentDirectory='content'] - Content directory name. Only applies to a newly created object.
|
|
124
126
|
* @property {string} [ocflVersion=c.OCFL_VERSION] - Ocfl version. Only applies to a newly created object.
|
|
127
|
+
* @property {string[]} [fixityAlgorithms] - A default list of digest algorithm names to be added to the fixity block of objects under this storage.
|
|
125
128
|
*/
|
|
126
129
|
|
|
127
130
|
/**
|
|
128
131
|
* General implementation of {@link OcflStorage} that can uses different datastore backends.
|
|
129
132
|
* This class provides common functionalities for the subclasses and
|
|
130
133
|
* at the same provide encapsulation emulating private and protected methods.
|
|
131
|
-
* @implements {OcflStorage}
|
|
132
134
|
*/
|
|
133
135
|
class OcflStorageImpl extends OcflStorage {
|
|
134
136
|
|
|
@@ -158,7 +160,9 @@ class OcflStorageImpl extends OcflStorage {
|
|
|
158
160
|
if (this.#workspace && this.#workspace.startsWith(this.#root + path.sep) && !this.#workspace.startsWith(this.#root + path.sep + 'extensions' + path.sep)) {
|
|
159
161
|
throw new Error('[OcflStorage] config.workspace cannot be the same as or a subpath of config.root');
|
|
160
162
|
}
|
|
161
|
-
|
|
163
|
+
if (layout) {
|
|
164
|
+
this.#layout = layout instanceof OcflStorageLayout ? layout : this.#createLayout(layout);
|
|
165
|
+
}
|
|
162
166
|
this.ocflVersion = ocflVersion || OCFL_VERSION;
|
|
163
167
|
this.#objectConfig = objectConfig;
|
|
164
168
|
}
|
|
@@ -194,15 +198,14 @@ class OcflStorageImpl extends OcflStorage {
|
|
|
194
198
|
if (typeof opt === 'string') id = opt;
|
|
195
199
|
else ({ id, root } = opt);
|
|
196
200
|
let relObjectRoot = root || this.objectRoot(id);
|
|
197
|
-
return new
|
|
201
|
+
return new OcflObject({
|
|
198
202
|
id,
|
|
199
203
|
root: path.join(this.root, relObjectRoot),
|
|
200
204
|
// @todo:use extension workspace if not defined
|
|
201
205
|
workspace: this.#workspace ? path.join(this.#workspace, relObjectRoot) : undefined,
|
|
202
|
-
ocflVersion: this.ocflVersion,
|
|
206
|
+
ocflVersion: /** @type {OcflVersion} */(this.ocflVersion),
|
|
203
207
|
...this.#objectConfig
|
|
204
|
-
},
|
|
205
|
-
this.#store);
|
|
208
|
+
}, this.#store);
|
|
206
209
|
}
|
|
207
210
|
|
|
208
211
|
async exists() {
|
|
@@ -231,7 +234,8 @@ class OcflStorageImpl extends OcflStorage {
|
|
|
231
234
|
} catch (error) {
|
|
232
235
|
}
|
|
233
236
|
this.#layout = this.#createLayout(layoutConfig || layoutName);
|
|
234
|
-
|
|
237
|
+
const layoutDigestAlgorithm = this.#layout.get('digestAlgorithm');
|
|
238
|
+
if (layoutDigestAlgorithm) await OcflDigest.initSync(layoutDigestAlgorithm);
|
|
235
239
|
//@todo: load storage level extensions
|
|
236
240
|
|
|
237
241
|
return this;
|
|
@@ -258,6 +262,8 @@ class OcflStorageImpl extends OcflStorage {
|
|
|
258
262
|
if (!this.#layout) {
|
|
259
263
|
this.#layout = new DEFAULT_LAYOUT();
|
|
260
264
|
}
|
|
265
|
+
const layoutDigestAlgorithm = this.#layout.get('digestAlgorithm');
|
|
266
|
+
if (layoutDigestAlgorithm) await OcflDigest.initSync(layoutDigestAlgorithm);
|
|
261
267
|
let layout = { extension: this.#layout.name, description: this.#layout.description };
|
|
262
268
|
await this.#store.writeFile(path.join(this.root, OCFL_LAYOUT), JSON.stringify(layout, null, 2), 'utf8');
|
|
263
269
|
if (this.#layout.config) {
|
|
@@ -271,6 +277,9 @@ class OcflStorageImpl extends OcflStorage {
|
|
|
271
277
|
return this;
|
|
272
278
|
}
|
|
273
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Allow async iteration over all OCFL objects in this storage.
|
|
282
|
+
*/
|
|
274
283
|
[Symbol.asyncIterator]() {
|
|
275
284
|
let store = this.#store;
|
|
276
285
|
let workspace = this.#workspace;
|
|
@@ -299,10 +308,10 @@ class OcflStorageImpl extends OcflStorage {
|
|
|
299
308
|
if (nv) {
|
|
300
309
|
//ocfl object exists
|
|
301
310
|
// inventory exists
|
|
302
|
-
let object = new
|
|
311
|
+
let object = new OcflObject({
|
|
303
312
|
root: basePath,
|
|
304
313
|
workspace: workspace ? path.join(workspace, path.relative(root, basePath)) : undefined,
|
|
305
|
-
ocflVersion: nv,
|
|
314
|
+
ocflVersion: /** @type {OcflVersion} */(nv),
|
|
306
315
|
...config
|
|
307
316
|
}, store);
|
|
308
317
|
//await object.load();
|
package/lib/store.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const {
|
|
3
|
-
const { parallelize, dataSourceAsIterable } = require('./utils');
|
|
2
|
+
const { parallelize, joinTypedArray } = require('./utils');
|
|
4
3
|
|
|
5
4
|
const emptyOptions = {};
|
|
6
5
|
|
|
@@ -9,6 +8,7 @@ const emptyOptions = {};
|
|
|
9
8
|
* such as local file system and cloud storage
|
|
10
9
|
*/
|
|
11
10
|
class OcflStore {
|
|
11
|
+
/** @type {Map<Object, OcflStore>} */
|
|
12
12
|
static instances;
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -52,7 +52,7 @@ class OcflStore {
|
|
|
52
52
|
* @param {string} filePath - Absolute path
|
|
53
53
|
* @param {*} options
|
|
54
54
|
* @return {Promise<import('fs').ReadStream>}
|
|
55
|
-
* @deprecated
|
|
55
|
+
* @deprecated Use {@link createReadable()} instead
|
|
56
56
|
*/
|
|
57
57
|
async createReadStream(filePath, options) { throw new Error('Not Implemented'); }
|
|
58
58
|
|
|
@@ -61,7 +61,7 @@ class OcflStore {
|
|
|
61
61
|
* @param {string} relPath
|
|
62
62
|
* @param {*} options
|
|
63
63
|
* @return {Promise<import('fs').WriteStream>}
|
|
64
|
-
* @deprecated
|
|
64
|
+
* @deprecated Use {@link createWritable()} instead
|
|
65
65
|
*/
|
|
66
66
|
async createWriteStream(relPath, options) { throw new Error('Not Implemented'); }
|
|
67
67
|
|
|
@@ -84,24 +84,27 @@ class OcflStore {
|
|
|
84
84
|
/**
|
|
85
85
|
* Provide a common interface to read a file inside an object.
|
|
86
86
|
* A concrete subclass MAY implement this method to provide read access to its underlying storage backend.
|
|
87
|
-
* The default implementation uses the {@linkcode OcflStore#
|
|
87
|
+
* The default implementation uses the {@linkcode OcflStore#createReadable} method.
|
|
88
88
|
* @param {string} filePath
|
|
89
89
|
* @param {*} [options]
|
|
90
90
|
*/
|
|
91
91
|
async readFile(filePath, options) {
|
|
92
|
-
|
|
93
|
-
let
|
|
92
|
+
const rs = await this.createReadable(filePath, options);
|
|
93
|
+
let buffer = (typeof options === 'string' || options.encoding) ? '' : new Uint8Array(new ArrayBuffer(0, { maxByteLength: 16 * 1024 }));
|
|
94
94
|
for await (const chunk of rs) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
if (typeof chunk === 'string') {
|
|
96
|
+
if (typeof buffer !== 'string') buffer = new TextDecoder().decode(buffer);
|
|
97
|
+
buffer += chunk;
|
|
98
|
+
} else {
|
|
99
|
+
if (typeof buffer === 'string') buffer = new TextEncoder().encode(buffer);
|
|
100
|
+
// @ts-ignore
|
|
101
|
+
buffer = joinTypedArray(buffer, chunk);
|
|
102
|
+
}
|
|
101
103
|
}
|
|
104
|
+
return buffer;
|
|
102
105
|
}
|
|
103
106
|
|
|
104
|
-
/** @typedef {string|Buffer|
|
|
107
|
+
/** @typedef {string|Buffer|Uint8Array|DataView|AsyncIterable|Iterable|ReadableStream} WriteFileData */
|
|
105
108
|
/**
|
|
106
109
|
* Provide a common interface to write a file inside an object.
|
|
107
110
|
* A concrete subclass MAY implement this method to provide write access to its underlying storage backend.
|
|
@@ -111,10 +114,15 @@ class OcflStore {
|
|
|
111
114
|
* @param {Object|string} options
|
|
112
115
|
*/
|
|
113
116
|
async writeFile(filePath, data, options) {
|
|
114
|
-
var source = dataSourceAsIterable(data);
|
|
115
117
|
await this.mkdir(path.dirname(filePath));
|
|
116
|
-
const target = await this.
|
|
117
|
-
|
|
118
|
+
const target = await this.createWritable(filePath, options);
|
|
119
|
+
if (data instanceof ReadableStream) {
|
|
120
|
+
await data.pipeTo(target);
|
|
121
|
+
} else {
|
|
122
|
+
const w = target.getWriter();
|
|
123
|
+
await w.write(data);
|
|
124
|
+
await w.close();
|
|
125
|
+
}
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
/**
|
|
@@ -124,7 +132,7 @@ class OcflStore {
|
|
|
124
132
|
* @param {string} target - destination filename of the copy operation
|
|
125
133
|
*/
|
|
126
134
|
async copyFile(source, target) {
|
|
127
|
-
await this.writeFile(target, await this.
|
|
135
|
+
await this.writeFile(target, await this.createReadable(source));
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
/**
|