@ocfl/ocfl 0.2.0 → 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/lib/inventory.js CHANGED
@@ -1,55 +1,72 @@
1
+ 'use strict';
2
+
1
3
  //@ts-check
4
+ /** @typedef {import('./types').Inventory} Inventory */
5
+ /** @typedef {import('./types').InventoryDigests} InventoryDigests */
2
6
 
3
- const path = require('path');
4
- const { digestSync } = require('./digest.js').OcflDigest;
5
- //const {createHash} = require('crypto');
6
-
7
- function nextVersion(currentVersion) {
8
- let zeroPaddingLength = 0;
9
- let currentVer = currentVersion.replace('v', '');
10
- if (currentVer.startsWith('0')) zeroPaddingLength = currentVer.length;
11
- let newVer = '' + (parseInt(currentVer) + 1);
12
- newVer = 'v' + (zeroPaddingLength ? newVer.padStart(zeroPaddingLength, '0') : newVer);
13
- return newVer;
14
- }
7
+ class VersionNumber {
8
+ value;
9
+ zeroPaddingLength;
10
+ /**
11
+ *
12
+ * @param {number} num
13
+ * @param {number} [zeroPaddingLength]
14
+ */
15
+ constructor(num, zeroPaddingLength) {
16
+ this.value = num;
17
+ this.zeroPaddingLength = zeroPaddingLength;
18
+ }
19
+ valueOf() {
20
+ return this.value;
21
+ }
22
+ toString() {
23
+ return 'v' + (this.zeroPaddingLength ? (this.value + '').padStart(this.zeroPaddingLength, '0') : this.value);
24
+ }
25
+ next() {
26
+ return new VersionNumber(this.value + 1, this.zeroPaddingLength);
27
+ }
28
+ previous() {
29
+ const n = this.value - 1;
30
+ if (n > 0) return new VersionNumber(n, this.zeroPaddingLength);
31
+ }
32
+ /**
33
+ *
34
+ * @param {number} num
35
+ * @param {number} zeroPaddingLength
36
+ */
37
+ static fromInt(num, zeroPaddingLength) {
38
+ return new VersionNumber(num, zeroPaddingLength);
39
+ }
40
+ /**
41
+ *
42
+ * @param {string} versionLabel
43
+ */
44
+ static fromString(versionLabel) {
45
+ const label = versionLabel.replace('v', '');
46
+ let zeroPaddingLength;
47
+ if (label.startsWith('0')) zeroPaddingLength = label.length;
48
+ return new VersionNumber(parseInt(label), zeroPaddingLength);
49
+ }
50
+
51
+ static next(versionLabel) {
52
+ return VersionNumber.fromString(versionLabel).next();
53
+ }
15
54
 
16
- function prevVersion(currentVersion) {
17
- let zeroPaddingLength = 0;
18
- let currentVer = currentVersion.replace('v', '');
19
- if (currentVer.startsWith('0')) zeroPaddingLength = currentVer.length;
20
- let prevVer = '' + (parseInt(currentVer) - 1);
21
- prevVer = 'v' + (zeroPaddingLength ? prevVer.padStart(zeroPaddingLength, '0') : prevVer);
22
- return prevVer;
55
+ static previous(versionLabel) {
56
+ return VersionNumber.fromString(versionLabel).previous();
57
+ }
23
58
  }
24
59
 
25
60
  /**
26
61
  * @typedef {{logicalPath?: string; digest?: string; contentPath?: string;}} FileRef
27
62
  */
28
63
 
29
- /**
30
- * @typedef {Object.<string, string[]>} InventoryState
31
- */
32
-
33
- /**
34
- * @typedef {Object} InventoryVersion
35
- * @property {string} [created]
36
- * @property {InventoryState} state
37
- * @property {string} [message]
38
- * @property {{name:string, address:string}} [user]
39
- */
40
-
41
- /**
42
- * @typedef {Object} Inventory
43
- * @property {string} id - A unique identifier for the OCFL Object.
44
- * @property {string} type - The URI of the inventory section of the specification version matching the object conformance declaration.
45
- * @property {string} digestAlgorithm - The digest algorithm used for calculating digests for content-addressing within the OCFL Object and for the Inventory Digest.
46
- * @property {string} head - The version directory name of the most recent version of the object.
47
- * @property {string} [contentDirectory='content'] - The name of the designated content directory within the version directories.
48
- * @property {InventoryState} manifest
49
- * @property {Object.<string, InventoryVersion>} versions
50
- * @property {Object} [fixity]
51
- */
52
-
64
+ function findKey(file, obj) {
65
+ for (let digest in obj) {
66
+ const arr = obj[digest];
67
+ if (arr.includes(file)) return digest;
68
+ }
69
+ }
53
70
 
54
71
  /**
55
72
  * Wrapper class for inventory data
@@ -57,30 +74,121 @@ function prevVersion(currentVersion) {
57
74
  class OcflObjectInventory {
58
75
  /** @type {Inventory} */
59
76
  #data;
60
- /** @type {InventoryVersion} */
61
- #version;
77
+ /** @type {{[key: string]: {[key:string]: {[key:string]: string}}}} */
78
+ #fileIndex = {};
79
+
80
+ metadata;
62
81
 
63
82
  /**
64
- * Create a mutable inventory and increase the inventory version number
65
- * @param {Object} data
66
- * @param {string} data.id
67
- * @param {string} data.type
68
- * @param {string} data.digestAlgorithm
69
- * @param {string} [data.head]
70
- * @param {string} [data.contentDirectory]
71
- * @param {Object.<string, string[]>} [data.manifest]
72
- * @param {Object.<string, InventoryVersion>} [data.versions]
73
- * @param {Object} [data.fixity]
83
+ * Helper function to find the digest by logical file path and cached it by indexing
84
+ * @param {string} logicalPath
85
+ * @param {string} version
86
+ * @param {boolean} [onlyUsecache]
87
+ */
88
+ _indexGet(logicalPath, version, onlyUsecache) {
89
+ let digests = this.#fileIndex[version]?.[logicalPath];
90
+ if (!digests && !onlyUsecache) {
91
+ // search and then index
92
+ const fixity = this.fixity;
93
+ const algo = this.digestAlgorithm;
94
+ const digestArr = [];
95
+ const mainDigest = findKey(logicalPath, this.versionState(version));
96
+ if (mainDigest) {
97
+ digestArr.push([algo, mainDigest]);
98
+ if (fixity) {
99
+ const cp = this.getContentPath(mainDigest);
100
+ for (const fixityDigest in fixity) {
101
+ const digest = findKey(cp, fixity[fixityDigest]);
102
+ if (digest) digestArr.push([fixityDigest, digest]);
103
+ }
104
+ }
105
+ }
106
+ if (digestArr.length > 0) {
107
+ const verIndex = this.#fileIndex[version] = this.#fileIndex[version] || {};
108
+ digests = verIndex[logicalPath] = Object.fromEntries(digestArr);
109
+ }
110
+ }
111
+ return digests;
112
+ }
113
+
114
+ _indexAdd(logicalPath, version, digests) {
115
+ this.#fileIndex[version][logicalPath] = digests;
116
+ }
117
+
118
+ _indexRemove(logicalPath, version = this.head) {
119
+ const val = this.#fileIndex[version][logicalPath];
120
+ delete this.#fileIndex[version][logicalPath];
121
+ return val;
122
+ }
123
+
124
+ _indexBuild() {
125
+ // index fixity contentPaths
126
+ const fixity = this.fixity;
127
+ const fixityIndex = {};
128
+ if (fixity) {
129
+ for (const fixityAlgo in fixity) {
130
+ const digestBlock = fixity[fixityAlgo];
131
+ const fixityIndexDigest = fixityIndex[fixityAlgo] = {};
132
+ for (const digest in digestBlock) {
133
+ const cps = digestBlock[digest];
134
+ for (const cp of cps) {
135
+ fixityIndexDigest[cp] = digest;
136
+ }
137
+ }
138
+ }
139
+ }
140
+ // index logicalPaths
141
+ // { v1: { 'dir/file.txt': { sha512: 'abcdef' } } }
142
+ const index = this.#fileIndex = {};
143
+ const algo = this.digestAlgorithm;
144
+ for (const verName in this.versions) {
145
+ const ver = this.versions[verName];
146
+ const indexVer = index[verName] = {};
147
+ for (const digest in ver.state) {
148
+ for (const lp of ver.state[digest]) {
149
+ const file = indexVer[lp] = { [algo]: digest };
150
+ if (fixity) {
151
+ for (const fixityAlgo in fixityIndex) {
152
+ const fixityIndexDigest = fixityIndex[fixityAlgo];
153
+ const cps = this.manifest[digest] || [];
154
+ for (const cp of cps) {
155
+ if (fixityIndexDigest[cp]) file[fixityAlgo] = fixityIndexDigest[cp];
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ _indexFindPaths(prefix, version = this.head) {
165
+ const res = [];
166
+ const index = this.#fileIndex[version];
167
+ const prefixWithSlash = prefix.endsWith('/') ? prefix : prefix + '/';
168
+ for (const lp in index) {
169
+ if (index[lp] && (lp === prefix || lp.startsWith(prefixWithSlash))) res.push(lp);
170
+ }
171
+ return res;
172
+ }
173
+
174
+
175
+ /**
176
+ * Create a new mutable OcflObjectInventory from the object config or clone an existing inventory,
177
+ * and increase the inventory version number. If data.head is undefined, starts from v1.
178
+ * @param {Partial<Inventory> | OcflObjectInventory} data
74
179
  * @param {boolean} [cleanState]
75
180
  */
76
181
  static newVersion(data, cleanState) {
182
+ if (data instanceof OcflObjectInventory) {
183
+ data = data.toJSON();
184
+ }
77
185
  if (!data.id || !data.digestAlgorithm || !data.type) throw new Error('Inventory must have an id, digestAlgorithm and type.');
78
186
  data.manifest = data.manifest || {};
79
187
  data.versions = data.versions || {};
80
188
  let prevVer;
81
189
  if (data.head) {
82
190
  prevVer = data.versions[data.head];
83
- data.head = nextVersion(data.head);
191
+ data.head = VersionNumber.next(data.head).toString();
84
192
  } else {
85
193
  data.head = 'v1';
86
194
  cleanState = true;
@@ -99,31 +207,39 @@ class OcflObjectInventory {
99
207
  */
100
208
  constructor(data, version) {
101
209
  this.#data = data;
102
- this.#version = data.versions[version || data.head];
210
+ this._indexBuild();
103
211
  }
104
212
 
105
213
  get id() { return this.#data.id }
106
214
  get type() { return this.#data.type }
107
215
  get digestAlgorithm() { return this.#data.digestAlgorithm }
108
216
  get head() { return this.#data.head }
217
+ /** The version number of the current head */
218
+ get versionNumber() { return VersionNumber.fromString(this.head); }
109
219
  get contentDirectory() { return this.#data.contentDirectory || 'content' }
110
220
  get manifest() { return this.#data.manifest }
111
221
  get versions() { return this.#data.versions }
222
+ get version() { return this.versions[this.head] }
112
223
  get fixity() { return this.#data.fixity }
113
- get state() { return this.#version.state }
114
- get created() { return this.#version.created }
115
- get message() { return this.#version.message }
116
- get user() { return this.#version.user }
117
- get prevVersion() { return prevVersion(this.head); }
224
+ set fixity(val) { this.#data.fixity = val; }
225
+ /** The state of the current version */
226
+ get state() { return this.version.state }
227
+ get created() { return this.version.created }
228
+ get message() { return this.version.message }
229
+ get user() { return this.version.user }
230
+ //get nextVersion() { return this.versionNumber.next().toString(); }
231
+ //get prevVersion() { return this.versionNumber.previous()?.toString(); }
118
232
 
119
233
  /** Return true if there has been actual change to the state of current version compared to the previous version */
120
234
  get isChanged() {
121
- let diff = this.diffState(this.head, prevVersion(this.head));
235
+ const prev = this.versionNumber.previous()?.toString();
236
+ if (!prev) return true;
237
+ let diff = this.diffState(this.head, prev);
122
238
  return Object.keys(diff[0]).length > 0 || Object.keys(diff[1]).length > 0;
123
239
  }
124
- set created(val) { this.#version.created = val; }
125
- set message(val) { this.#version.message = val; }
126
- set user(val) { this.#version.user = val; }
240
+ set created(val) { this.version.created = val; }
241
+ set message(val) { this.version.message = val; }
242
+ set user(val) { this.version.user = val; }
127
243
 
128
244
  /**
129
245
  * Return the state of the specified version
@@ -138,11 +254,6 @@ class OcflObjectInventory {
138
254
  return JSON.stringify(this.#data, null, 2);
139
255
  }
140
256
 
141
- /** Compute digest from current state of this inventory object*/
142
- digest() {
143
- return digestSync(this.#data.digestAlgorithm, this.toString());
144
- }
145
-
146
257
  /**
147
258
  * @return {Inventory}
148
259
  */
@@ -170,7 +281,9 @@ class OcflObjectInventory {
170
281
  logicalPath,
171
282
  version,
172
283
  digest,
173
- contentPath
284
+ contentPath,
285
+ fixity: this.getFixity(logicalPath, version),
286
+ ...this.metadata?.[contentPath]
174
287
  };
175
288
  }
176
289
  }
@@ -198,21 +311,64 @@ class OcflObjectInventory {
198
311
  }
199
312
 
200
313
  /**
201
- * Get the content digest of the specified logicalPath
314
+ * Get the digest of a content represented by the logicalPath of the specified version
202
315
  * @param {string} logicalPath
203
316
  * @param {string} [versionName]
204
- * @returns
205
317
  */
206
- getDigest(logicalPath, versionName) {
207
- let state = this.versionState(versionName);
208
- return Object.keys(state).find(d => state[d].includes(logicalPath));
318
+ getDigest(logicalPath, versionName = this.head) {
319
+ return this._indexGet(logicalPath, versionName, true)?.[this.digestAlgorithm];
320
+ }
321
+
322
+ /**
323
+ * Get the file information by the logicalPath of the specified version
324
+ * @param {string} logicalPath
325
+ * @param {string} [versionName]
326
+ */
327
+ getFile(logicalPath, versionName = this.head) {
328
+ const digest = this.getDigest(logicalPath, versionName);
329
+ const contentPath = this.getContentPath(digest);
330
+ const version = versionName;
331
+ if (digest) {
332
+ return {
333
+ logicalPath,
334
+ version,
335
+ digest,
336
+ contentPath,
337
+ fixity: this.getFixity(logicalPath, version),
338
+ ...this.metadata?.[contentPath]
339
+ }
340
+ }
341
+ }
342
+
343
+ getFileByDigest(digest, versionName) {
344
+ if (versionName) return this.getFile(this.versions[versionName].state[digest]?.[0], versionName);
345
+ for (const version in this.versions) {
346
+ const logicalPath = this.versions[version].state[digest]?.[0];
347
+ if (logicalPath) return this.getFile(logicalPath, version);
348
+ }
349
+ }
350
+
351
+ getFileByContentPath(contentPath, versionName) {
352
+ for (const digest in this.manifest) {
353
+ if (this.manifest[digest].includes(contentPath)) return this.getFileByDigest(digest, versionName);
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Get the fixity of a content represented by the logicalPath of the specified version
359
+ * @param {string} logicalPath
360
+ * @param {string} [versionName]
361
+ */
362
+ getFixity(logicalPath, versionName = this.head) {
363
+ const { [this.digestAlgorithm]: digest, ...fixity } = this._indexGet(logicalPath, versionName, true) || {};
364
+ return fixity;
209
365
  }
210
366
 
211
367
  /**
212
368
  * Compare the differences between the state of two versions
213
369
  * @param {string} version0
214
370
  * @param {string} version1
215
- * @return {[InventoryState, InventoryState]} - A tuple of [A, B] where A contains files
371
+ * @return {[InventoryDigests, InventoryDigests]} - A tuple of [A, B] where A contains files
216
372
  * that are in `version0` but not in `version1` and B contains files
217
373
  * that are in `version1` but not in `version0`
218
374
  */
@@ -237,7 +393,7 @@ class OcflObjectInventory {
237
393
  return Object.fromEntries(r);
238
394
  });
239
395
 
240
- return /** @type {[InventoryState, InventoryState]} */(result);
396
+ return /** @type {[InventoryDigests, InventoryDigests]} */(result);
241
397
  }
242
398
  }
243
399
 
@@ -246,41 +402,27 @@ class OcflObjectInventory {
246
402
  * Wrapper class with mutable methods for inventory data
247
403
  */
248
404
  class OcflObjectInventoryMut extends OcflObjectInventory {
249
- /** @type {Object.<string,string>} */
250
- #byPath;
251
405
 
252
406
  constructor(data) {
253
407
  super(data);
254
- //index logicalPaths
255
- let byPath = this.#byPath = {};
256
- let state = this.state;
257
- for (let d in state) {
258
- let files = state[d];
259
- for (let lp of files) {
260
- byPath[lp] = d;
261
- }
262
- }
263
- }
264
-
265
- /**
266
- * Get the digest of a content represented by the logicalPath of the specified version
267
- * @param {string} logicalPath
268
- * @param {string} [versionName]
269
- * @return {string}
270
- */
271
- getDigest(logicalPath, versionName) {
272
- if (versionName) return super.getDigest(logicalPath, versionName);
273
- else return this.#byPath[logicalPath];
274
408
  }
275
409
 
276
410
  /**
277
411
  * Add a new file to the inventory or update the digest of an existing file
278
412
  * @param {string} digest
279
413
  * @param {string} logicalPath
414
+ * @param {{[x: string]: string}} [fixity]
280
415
  * @return {boolean} - true if the state is changed
281
416
  */
282
- add(logicalPath, digest) {
283
- let state = this.state;
417
+ add(logicalPath, digest, fixity) {
418
+ const state = this.state;
419
+ const contentPath = this.head + '/' + this.contentDirectory + '/' + logicalPath;
420
+ // add fixity info
421
+ if (fixity) {
422
+ for (const digestName in fixity) {
423
+ this.addFixity(contentPath, digestName, fixity[digestName]);
424
+ }
425
+ }
284
426
  if (state[digest]) {
285
427
  // this is a file with identical content but different name
286
428
  if (state[digest].indexOf(logicalPath) < 0) state[digest].push(logicalPath);
@@ -290,19 +432,38 @@ class OcflObjectInventoryMut extends OcflObjectInventory {
290
432
  if (mf && mf.length > 0) {
291
433
  // this is a reinstatement
292
434
  } else {
293
- // normal addition
294
- this.manifest[digest] = [path.join(this.head, this.contentDirectory, logicalPath)];
435
+ // normal addition of a new file
436
+ this.manifest[digest] = [contentPath];
295
437
  }
296
- if (this.#byPath[logicalPath]) {
438
+ if (this.getDigest(logicalPath)) {
297
439
  // this is an update, remove old digest
298
440
  this.delete(logicalPath);
299
441
  }
300
442
  state[digest] = [logicalPath];
301
443
  }
302
- this.#byPath[logicalPath] = digest;
444
+ this._indexAdd(logicalPath, this.head, { [this.digestAlgorithm]: digest, ...fixity });
303
445
  return true;
304
446
  }
305
447
 
448
+ /**
449
+ * Low-level method to add a set of digest entries associated to file into the fixity block
450
+ * @param {string} contentPath
451
+ * @param {string} digestName
452
+ * @param {string} digestValue
453
+ */
454
+ addFixity(contentPath, digestName, digestValue) {
455
+ if (!digestValue || !digestName) return;
456
+ const fixityBlock = this.fixity = this.fixity || {};
457
+ const digestBlock = fixityBlock[digestName] = fixityBlock[digestName] || {};
458
+ if (digestBlock[digestValue]) {
459
+ if (!digestBlock[digestValue].includes(contentPath)) {
460
+ digestBlock[digestValue].push(contentPath);
461
+ }
462
+ } else {
463
+ digestBlock[digestValue] = [contentPath];
464
+ }
465
+ }
466
+
306
467
  /**
307
468
  * Rename a logical path. This will overwrite dest if exists
308
469
  * @param {string} srcLogicalPath
@@ -311,18 +472,17 @@ class OcflObjectInventoryMut extends OcflObjectInventory {
311
472
  rename(srcLogicalPath, destLogicalPath) {
312
473
  let srcPrefix = srcLogicalPath.endsWith('/') ? srcLogicalPath : srcLogicalPath + '/';
313
474
  let destPrefix = destLogicalPath.endsWith('/') ? destLogicalPath : destLogicalPath + '/';
314
- let srcList = Object.keys(this.#byPath).filter(p => p.startsWith(srcPrefix));
475
+ let srcList = this._indexFindPaths(srcPrefix);
315
476
  let pairs = srcList.map(src => [src, destPrefix + src.slice(srcPrefix.length)]);
316
477
  if (pairs.length === 0) pairs.push([srcLogicalPath, destLogicalPath]);
317
478
  for (let [src, dest] of pairs) {
318
- let digest = this.#byPath[src];
479
+ let digest = this.getDigest(src);
319
480
  let files = this.state[digest];
320
481
  let index = files?.indexOf(src);
321
482
  if (index >= 0) {
322
483
  this.delete(dest); //overwrite
323
484
  files[index] = dest;
324
- this.#byPath[dest] = digest;
325
- this.#byPath[src] = null;
485
+ this._indexAdd(dest, this.head, this._indexRemove(src, this.head));
326
486
  } else {
327
487
  /** @todo decide: throw error or silently ignore non-existant file? */
328
488
  }
@@ -337,21 +497,21 @@ class OcflObjectInventoryMut extends OcflObjectInventory {
337
497
  copy(srcLogicalPath, destLogicalPath) {
338
498
  let srcPrefix = srcLogicalPath.endsWith('/') ? srcLogicalPath : srcLogicalPath + '/';
339
499
  let destPrefix = destLogicalPath.endsWith('/') ? destLogicalPath : destLogicalPath + '/';
340
- let srcList = Object.keys(this.#byPath).filter(p => p.startsWith(srcPrefix));
500
+ let srcList = this._indexFindPaths(srcPrefix);
341
501
  let pairs = srcList.map(src => [src, destPrefix + src.slice(srcPrefix.length)]);
342
502
  if (pairs.length === 0) pairs.push([srcLogicalPath, destLogicalPath]);
343
503
  for (let [src, dest] of pairs) {
344
504
  if (!dest) throw new Error(`Target logical path cannot be empty`);
345
- let digest = this.#byPath[src];
505
+ let digest = this._indexGet(src, this.head, true);
346
506
  if (!digest) throw new Error(`Source logical path "${src}" does not exist in object [${this.id}]`);
347
507
  //console.log(this.state);
348
- let files = this.state[digest];
508
+ let files = this.state[digest[this.digestAlgorithm]];
349
509
  let index = files?.indexOf(dest);
350
510
  if (index >= 0) {
351
511
  /** @todo decide: throw error or silently ignore existing dest file? */
352
512
  } else {
353
513
  files.push(dest);
354
- this.#byPath[dest] = digest;
514
+ this._indexAdd(dest, this.head, digest);
355
515
  }
356
516
  }
357
517
  }
@@ -359,20 +519,23 @@ class OcflObjectInventoryMut extends OcflObjectInventory {
359
519
  /**
360
520
  * Delete a file
361
521
  * @param {string} logicalPath
522
+ * @param {string} [version]
362
523
  */
363
- delete(logicalPath) {
364
- let prefix = logicalPath.endsWith('/') ? logicalPath : logicalPath + '/';
365
- let names = this.getDigest(logicalPath) ? [logicalPath] :
366
- Object.keys(this.#byPath).filter(p => p.startsWith(prefix));
524
+ delete(logicalPath, version = this.head) {
525
+ const names = this._indexFindPaths(logicalPath, version);
367
526
  let count = 0;
527
+ const state = this.versionState(version);
368
528
  for (let lp of names) {
369
- let digest = this.#byPath[lp];
370
- let files = this.state[digest];
371
- let index = files?.indexOf(lp);
529
+ const digest = this.getDigest(lp, version);
530
+ const files = state[digest];
531
+ const index = files?.indexOf(lp);
372
532
  if (index >= 0) {
373
- files.splice(index, 1);
374
- if (files.length === 0) delete this.state[digest];
375
- this.#byPath[lp] = null;
533
+ if (files.length === 1) {
534
+ delete state[digest];
535
+ } else {
536
+ files.splice(index, 1);
537
+ }
538
+ this._indexRemove(lp, version);
376
539
  count++;
377
540
  }
378
541
  }
@@ -380,19 +543,17 @@ class OcflObjectInventoryMut extends OcflObjectInventory {
380
543
  }
381
544
 
382
545
  reinstate(logicalPath, versionName) {
383
- let prefix = logicalPath.endsWith('/') ? logicalPath : logicalPath + '/';
384
- let digest = this.getDigest(logicalPath, versionName);
385
- /** @type {FileRef[]} */
386
- let files = digest ? [{ logicalPath, digest }] :
387
- [...this.find(f => f.logicalPath.startsWith(prefix), versionName)];
546
+ let names = this._indexFindPaths(logicalPath, versionName);
547
+ //let digest = this.getDigest(logicalPath, versionName);
388
548
  let state = this.state;
389
549
  let count = 0;
390
- for (let f of files) {
391
- if (this.getDigest(f.logicalPath)) {
392
- let c = this.delete(f.logicalPath);
550
+ for (let lp of names) {
551
+ const digests = this._indexGet(lp, versionName);
552
+ if (digests) {
553
+ let c = this.delete(lp);
393
554
  }
394
- state[f.digest] = [f.logicalPath];
395
- this.#byPath[f.logicalPath] = f.digest;
555
+ state[digests[this.digestAlgorithm]] = [lp];
556
+ this._indexAdd(lp, this.head, digests);
396
557
  count++;
397
558
  }
398
559
  return count;
@@ -400,4 +561,4 @@ class OcflObjectInventoryMut extends OcflObjectInventory {
400
561
 
401
562
  }
402
563
 
403
- module.exports = { OcflObjectInventory, OcflObjectInventoryMut };
564
+ module.exports = { OcflObjectInventory, OcflObjectInventoryMut, VersionNumber };