@ocfl/ocfl 0.3.0 → 1.0.0-beta.1
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/LICENSE +674 -0
- package/README.md +24 -9
- package/dist/constants.d.ts +15 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -0
- package/dist/digest.d.ts +58 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +204 -0
- package/dist/digest.js.map +1 -0
- package/dist/error.d.ts +5 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +9 -0
- package/dist/error.js.map +1 -0
- package/dist/extension.d.ts +83 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +170 -0
- package/dist/extension.js.map +1 -0
- package/dist/extensions/0002-flat-direct-storage-layout.d.ts +12 -0
- package/dist/extensions/0002-flat-direct-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0002-flat-direct-storage-layout.js +34 -0
- package/dist/extensions/0002-flat-direct-storage-layout.js.map +1 -0
- package/dist/extensions/0003-hash-and-id-n-tuple-storage-layout.d.ts +15 -0
- package/dist/extensions/0003-hash-and-id-n-tuple-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0003-hash-and-id-n-tuple-storage-layout.js +49 -0
- package/dist/extensions/0003-hash-and-id-n-tuple-storage-layout.js.map +1 -0
- package/dist/extensions/0004-hashed-n-tuple-storage-layout.d.ts +17 -0
- package/dist/extensions/0004-hashed-n-tuple-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0004-hashed-n-tuple-storage-layout.js +54 -0
- package/dist/extensions/0004-hashed-n-tuple-storage-layout.js.map +1 -0
- package/dist/extensions/0006-flat-omit-prefix-storage-layout.d.ts +6 -0
- package/dist/extensions/0006-flat-omit-prefix-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0006-flat-omit-prefix-storage-layout.js +8 -0
- package/dist/extensions/0006-flat-omit-prefix-storage-layout.js.map +1 -0
- package/dist/extensions/0007-n-tuple-omit-prefix-storage-layout.d.ts +6 -0
- package/dist/extensions/0007-n-tuple-omit-prefix-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0007-n-tuple-omit-prefix-storage-layout.js +8 -0
- package/dist/extensions/0007-n-tuple-omit-prefix-storage-layout.js.map +1 -0
- package/dist/extensions/0009-digest-algorithms.d.ts +9 -0
- package/dist/extensions/0009-digest-algorithms.d.ts.map +1 -0
- package/dist/extensions/0009-digest-algorithms.js +11 -0
- package/dist/extensions/0009-digest-algorithms.js.map +1 -0
- package/dist/extensions/000N-path-direct-storage-layout.d.ts +16 -0
- package/dist/extensions/000N-path-direct-storage-layout.d.ts.map +1 -0
- package/dist/extensions/000N-path-direct-storage-layout.js +46 -0
- package/dist/extensions/000N-path-direct-storage-layout.js.map +1 -0
- package/dist/extensions/index.d.ts +23 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +8 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/file.d.ts +87 -0
- package/dist/file.d.ts.map +1 -0
- package/dist/file.js +118 -0
- package/dist/file.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/inventory.d.ts +155 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +591 -0
- package/dist/inventory.js.map +1 -0
- package/dist/object.d.ts +188 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/object.js +425 -0
- package/dist/object.js.map +1 -0
- package/dist/storage.d.ts +94 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +302 -0
- package/dist/storage.js.map +1 -0
- package/dist/store.d.ts +100 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +157 -0
- package/dist/store.js.map +1 -0
- package/dist/transaction.d.ts +155 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +395 -0
- package/dist/transaction.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +85 -0
- package/dist/utils.js.map +1 -0
- package/dist/validation.d.ts +32 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +31 -0
- package/dist/validation.js.map +1 -0
- package/package.json +31 -50
- package/src/constants.ts +16 -0
- package/src/digest.ts +258 -0
- package/src/error.ts +9 -0
- package/src/extension.ts +206 -0
- package/{lib/extensions/0002-flat-direct-storage-layout.js → src/extensions/0002-flat-direct-storage-layout.ts} +15 -22
- package/src/extensions/0003-hash-and-id-n-tuple-storage-layout.ts +56 -0
- package/src/extensions/0004-hashed-n-tuple-storage-layout.ts +59 -0
- package/src/extensions/0006-flat-omit-prefix-storage-layout.ts +9 -0
- package/src/extensions/0007-n-tuple-omit-prefix-storage-layout.ts +9 -0
- package/src/extensions/0009-digest-algorithms.ts +12 -0
- package/src/extensions/000N-path-direct-storage-layout.ts +51 -0
- package/src/extensions/index.ts +24 -0
- package/src/file.ts +161 -0
- package/src/index.ts +92 -0
- package/src/inventory.ts +634 -0
- package/src/object.ts +473 -0
- package/src/storage.ts +342 -0
- package/src/store.ts +199 -0
- package/src/transaction.ts +456 -0
- package/{lib/types.d.ts → src/types.ts} +28 -74
- package/src/utils.ts +94 -0
- package/src/validation.ts +46 -0
- package/index.js +0 -129
- package/index.mjs +0 -18
- package/lib/constants.js +0 -24
- package/lib/digest.js +0 -307
- package/lib/enum.js +0 -90
- package/lib/error.js +0 -19
- package/lib/extension.js +0 -194
- package/lib/extensions/0003-hash-and-id-n-tuple-storage-layout.js +0 -60
- package/lib/extensions/0004-hashed-n-tuple-storage-layout.js +0 -63
- package/lib/extensions/0005-mutable-head.js +0 -0
- package/lib/extensions/0006-flat-omit-prefix-storage-layout.js +0 -7
- package/lib/extensions/0007-n-tuple-omit-prefix-storage-layout.js +0 -7
- package/lib/extensions/0008-schema-registry.js +0 -0
- package/lib/extensions/0009-digest-algorithms.js +0 -18
- package/lib/extensions/000N-path-direct-storage-layout.js +0 -61
- package/lib/extensions/index.js +0 -13
- package/lib/file.js +0 -129
- package/lib/index.js +0 -13
- package/lib/inventory.js +0 -564
- package/lib/object.js +0 -451
- package/lib/storage.js +0 -338
- package/lib/store.js +0 -191
- package/lib/transaction.js +0 -448
- package/lib/utils.js +0 -123
- package/lib/validation.js +0 -174
package/lib/inventory.js
DELETED
|
@@ -1,564 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
//@ts-check
|
|
4
|
-
/** @typedef {import('./types').Inventory} Inventory */
|
|
5
|
-
/** @typedef {import('./types').InventoryDigests} InventoryDigests */
|
|
6
|
-
|
|
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
|
-
}
|
|
54
|
-
|
|
55
|
-
static previous(versionLabel) {
|
|
56
|
-
return VersionNumber.fromString(versionLabel).previous();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @typedef {{logicalPath?: string; digest?: string; contentPath?: string;}} FileRef
|
|
62
|
-
*/
|
|
63
|
-
|
|
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
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Wrapper class for inventory data
|
|
73
|
-
*/
|
|
74
|
-
class OcflObjectInventory {
|
|
75
|
-
/** @type {Inventory} */
|
|
76
|
-
#data;
|
|
77
|
-
/** @type {{[key: string]: {[key:string]: {[key:string]: string}}}} */
|
|
78
|
-
#fileIndex = {};
|
|
79
|
-
|
|
80
|
-
metadata;
|
|
81
|
-
|
|
82
|
-
/**
|
|
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
|
|
179
|
-
* @param {boolean} [cleanState]
|
|
180
|
-
*/
|
|
181
|
-
static newVersion(data, cleanState) {
|
|
182
|
-
if (data instanceof OcflObjectInventory) {
|
|
183
|
-
data = data.toJSON();
|
|
184
|
-
}
|
|
185
|
-
if (!data.id || !data.digestAlgorithm || !data.type) throw new Error('Inventory must have an id, digestAlgorithm and type.');
|
|
186
|
-
data.manifest = data.manifest || {};
|
|
187
|
-
data.versions = data.versions || {};
|
|
188
|
-
let prevVer;
|
|
189
|
-
if (data.head) {
|
|
190
|
-
prevVer = data.versions[data.head];
|
|
191
|
-
data.head = VersionNumber.next(data.head).toString();
|
|
192
|
-
} else {
|
|
193
|
-
data.head = 'v1';
|
|
194
|
-
cleanState = true;
|
|
195
|
-
}
|
|
196
|
-
data.versions[data.head] = {
|
|
197
|
-
created: (new Date()).toISOString(),
|
|
198
|
-
state: cleanState ? {} : JSON.parse(JSON.stringify(prevVer.state))
|
|
199
|
-
};
|
|
200
|
-
return new OcflObjectInventoryMut(data);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Create a new inventory model
|
|
205
|
-
* @param {Inventory} data - Initial data of the inventory
|
|
206
|
-
* @param {string} [version] - Set the version, default to data.head
|
|
207
|
-
*/
|
|
208
|
-
constructor(data, version) {
|
|
209
|
-
this.#data = data;
|
|
210
|
-
this._indexBuild();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
get id() { return this.#data.id }
|
|
214
|
-
get type() { return this.#data.type }
|
|
215
|
-
get digestAlgorithm() { return this.#data.digestAlgorithm }
|
|
216
|
-
get head() { return this.#data.head }
|
|
217
|
-
/** The version number of the current head */
|
|
218
|
-
get versionNumber() { return VersionNumber.fromString(this.head); }
|
|
219
|
-
get contentDirectory() { return this.#data.contentDirectory || 'content' }
|
|
220
|
-
get manifest() { return this.#data.manifest }
|
|
221
|
-
get versions() { return this.#data.versions }
|
|
222
|
-
get version() { return this.versions[this.head] }
|
|
223
|
-
get fixity() { return this.#data.fixity }
|
|
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(); }
|
|
232
|
-
|
|
233
|
-
/** Return true if there has been actual change to the state of current version compared to the previous version */
|
|
234
|
-
get isChanged() {
|
|
235
|
-
const prev = this.versionNumber.previous()?.toString();
|
|
236
|
-
if (!prev) return true;
|
|
237
|
-
let diff = this.diffState(this.head, prev);
|
|
238
|
-
return Object.keys(diff[0]).length > 0 || Object.keys(diff[1]).length > 0;
|
|
239
|
-
}
|
|
240
|
-
set created(val) { this.version.created = val; }
|
|
241
|
-
set message(val) { this.version.message = val; }
|
|
242
|
-
set user(val) { this.version.user = val; }
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Return the state of the specified version
|
|
246
|
-
* @param {string} [version ] - The inventory version, default to the latest
|
|
247
|
-
*/
|
|
248
|
-
versionState(version) {
|
|
249
|
-
if (!version || version === 'latest') version = this.head;
|
|
250
|
-
return this.versions[version]?.state;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
toString() {
|
|
254
|
-
return JSON.stringify(this.#data, null, 2);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* @return {Inventory}
|
|
259
|
-
*/
|
|
260
|
-
toJSON() {
|
|
261
|
-
return JSON.parse(this.toString());
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
*
|
|
266
|
-
* @param {string} [version] - The version name
|
|
267
|
-
*/
|
|
268
|
-
*files(version) {
|
|
269
|
-
let state;
|
|
270
|
-
if (!version || version === 'latest') {
|
|
271
|
-
state = this.state;
|
|
272
|
-
version = this.head;
|
|
273
|
-
} else {
|
|
274
|
-
state = this.versions[version]?.state;
|
|
275
|
-
}
|
|
276
|
-
for (let digest in state) {
|
|
277
|
-
let files = state[digest];
|
|
278
|
-
let contentPath = this.getContentPath(digest);
|
|
279
|
-
for (let logicalPath of files) {
|
|
280
|
-
yield {
|
|
281
|
-
logicalPath,
|
|
282
|
-
version,
|
|
283
|
-
digest,
|
|
284
|
-
contentPath,
|
|
285
|
-
fixity: this.getFixity(logicalPath, version),
|
|
286
|
-
...this.metadata?.[contentPath]
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
*find(fn, version) {
|
|
293
|
-
for (let f of this.files(version)) {
|
|
294
|
-
if (fn(f)) yield f;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
clone() {
|
|
299
|
-
return new OcflObjectInventory(this.toJSON());
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Get the first content path found with the given digest from the manifest.
|
|
304
|
-
* Also can be used to check the existance of an entry with the specified digest in the manifest.
|
|
305
|
-
* @param {string} digest
|
|
306
|
-
* @return {string} - The first content path found in the array
|
|
307
|
-
*/
|
|
308
|
-
getContentPath(digest) {
|
|
309
|
-
let cp = this.manifest[digest];
|
|
310
|
-
if (cp && cp.length > 0) return cp[0];
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Get the digest of a content represented by the logicalPath of the specified version
|
|
315
|
-
* @param {string} logicalPath
|
|
316
|
-
* @param {string} [versionName]
|
|
317
|
-
*/
|
|
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;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Compare the differences between the state of two versions
|
|
369
|
-
* @param {string} version0
|
|
370
|
-
* @param {string} version1
|
|
371
|
-
* @return {[InventoryDigests, InventoryDigests]} - A tuple of [A, B] where A contains files
|
|
372
|
-
* that are in `version0` but not in `version1` and B contains files
|
|
373
|
-
* that are in `version1` but not in `version0`
|
|
374
|
-
*/
|
|
375
|
-
diffState(version0, version1) {
|
|
376
|
-
var state1 = this.versionState(version0) ?? {};
|
|
377
|
-
var state2 = this.versionState(version1) ?? {};
|
|
378
|
-
|
|
379
|
-
var result = [[state1, state2], [state2, state1]].map(([s1, s2]) => {
|
|
380
|
-
/** @type {Map<string, string[]>} */
|
|
381
|
-
let r = new Map();
|
|
382
|
-
for (let d in s1) {
|
|
383
|
-
let d1 = s1[d];
|
|
384
|
-
let d2 = s2[d];
|
|
385
|
-
let diff;
|
|
386
|
-
if (d2 && d2.length) {
|
|
387
|
-
diff = d1.filter(f => !d2.includes(f));
|
|
388
|
-
} else {
|
|
389
|
-
diff = d1.slice();
|
|
390
|
-
}
|
|
391
|
-
if (diff.length > 0) r.set(d, diff);
|
|
392
|
-
}
|
|
393
|
-
return Object.fromEntries(r);
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
return /** @type {[InventoryDigests, InventoryDigests]} */(result);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Wrapper class with mutable methods for inventory data
|
|
403
|
-
*/
|
|
404
|
-
class OcflObjectInventoryMut extends OcflObjectInventory {
|
|
405
|
-
|
|
406
|
-
constructor(data) {
|
|
407
|
-
super(data);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Add a new file to the inventory or update the digest of an existing file
|
|
412
|
-
* @param {string} digest
|
|
413
|
-
* @param {string} logicalPath
|
|
414
|
-
* @param {{[x: string]: string}} [fixity]
|
|
415
|
-
* @return {boolean} - true if the state is changed
|
|
416
|
-
*/
|
|
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
|
-
}
|
|
426
|
-
if (state[digest]) {
|
|
427
|
-
// this is a file with identical content but different name
|
|
428
|
-
if (state[digest].indexOf(logicalPath) < 0) state[digest].push(logicalPath);
|
|
429
|
-
else return false; // else, same digest and path, ignore
|
|
430
|
-
} else {
|
|
431
|
-
let mf = this.manifest[digest];
|
|
432
|
-
if (mf && mf.length > 0) {
|
|
433
|
-
// this is a reinstatement
|
|
434
|
-
} else {
|
|
435
|
-
// normal addition of a new file
|
|
436
|
-
this.manifest[digest] = [contentPath];
|
|
437
|
-
}
|
|
438
|
-
if (this.getDigest(logicalPath)) {
|
|
439
|
-
// this is an update, remove old digest
|
|
440
|
-
this.delete(logicalPath);
|
|
441
|
-
}
|
|
442
|
-
state[digest] = [logicalPath];
|
|
443
|
-
}
|
|
444
|
-
this._indexAdd(logicalPath, this.head, { [this.digestAlgorithm]: digest, ...fixity });
|
|
445
|
-
return true;
|
|
446
|
-
}
|
|
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
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Rename a logical path. This will overwrite dest if exists
|
|
469
|
-
* @param {string} srcLogicalPath
|
|
470
|
-
* @param {string} destLogicalPath
|
|
471
|
-
*/
|
|
472
|
-
rename(srcLogicalPath, destLogicalPath) {
|
|
473
|
-
let srcPrefix = srcLogicalPath.endsWith('/') ? srcLogicalPath : srcLogicalPath + '/';
|
|
474
|
-
let destPrefix = destLogicalPath.endsWith('/') ? destLogicalPath : destLogicalPath + '/';
|
|
475
|
-
let srcList = this._indexFindPaths(srcPrefix);
|
|
476
|
-
let pairs = srcList.map(src => [src, destPrefix + src.slice(srcPrefix.length)]);
|
|
477
|
-
if (pairs.length === 0) pairs.push([srcLogicalPath, destLogicalPath]);
|
|
478
|
-
for (let [src, dest] of pairs) {
|
|
479
|
-
let digest = this.getDigest(src);
|
|
480
|
-
let files = this.state[digest];
|
|
481
|
-
let index = files?.indexOf(src);
|
|
482
|
-
if (index >= 0) {
|
|
483
|
-
this.delete(dest); //overwrite
|
|
484
|
-
files[index] = dest;
|
|
485
|
-
this._indexAdd(dest, this.head, this._indexRemove(src, this.head));
|
|
486
|
-
} else {
|
|
487
|
-
/** @todo decide: throw error or silently ignore non-existant file? */
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Create an alias or reference of a logical path
|
|
494
|
-
* @param {string} srcLogicalPath
|
|
495
|
-
* @param {string} destLogicalPath
|
|
496
|
-
*/
|
|
497
|
-
copy(srcLogicalPath, destLogicalPath) {
|
|
498
|
-
let srcPrefix = srcLogicalPath.endsWith('/') ? srcLogicalPath : srcLogicalPath + '/';
|
|
499
|
-
let destPrefix = destLogicalPath.endsWith('/') ? destLogicalPath : destLogicalPath + '/';
|
|
500
|
-
let srcList = this._indexFindPaths(srcPrefix);
|
|
501
|
-
let pairs = srcList.map(src => [src, destPrefix + src.slice(srcPrefix.length)]);
|
|
502
|
-
if (pairs.length === 0) pairs.push([srcLogicalPath, destLogicalPath]);
|
|
503
|
-
for (let [src, dest] of pairs) {
|
|
504
|
-
if (!dest) throw new Error(`Target logical path cannot be empty`);
|
|
505
|
-
let digest = this._indexGet(src, this.head, true);
|
|
506
|
-
if (!digest) throw new Error(`Source logical path "${src}" does not exist in object [${this.id}]`);
|
|
507
|
-
//console.log(this.state);
|
|
508
|
-
let files = this.state[digest[this.digestAlgorithm]];
|
|
509
|
-
let index = files?.indexOf(dest);
|
|
510
|
-
if (index >= 0) {
|
|
511
|
-
/** @todo decide: throw error or silently ignore existing dest file? */
|
|
512
|
-
} else {
|
|
513
|
-
files.push(dest);
|
|
514
|
-
this._indexAdd(dest, this.head, digest);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Delete a file
|
|
521
|
-
* @param {string} logicalPath
|
|
522
|
-
* @param {string} [version]
|
|
523
|
-
*/
|
|
524
|
-
delete(logicalPath, version = this.head) {
|
|
525
|
-
const names = this._indexFindPaths(logicalPath, version);
|
|
526
|
-
let count = 0;
|
|
527
|
-
const state = this.versionState(version);
|
|
528
|
-
for (let lp of names) {
|
|
529
|
-
const digest = this.getDigest(lp, version);
|
|
530
|
-
const files = state[digest];
|
|
531
|
-
const index = files?.indexOf(lp);
|
|
532
|
-
if (index >= 0) {
|
|
533
|
-
if (files.length === 1) {
|
|
534
|
-
delete state[digest];
|
|
535
|
-
} else {
|
|
536
|
-
files.splice(index, 1);
|
|
537
|
-
}
|
|
538
|
-
this._indexRemove(lp, version);
|
|
539
|
-
count++;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
return count;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
reinstate(logicalPath, versionName) {
|
|
546
|
-
let names = this._indexFindPaths(logicalPath, versionName);
|
|
547
|
-
//let digest = this.getDigest(logicalPath, versionName);
|
|
548
|
-
let state = this.state;
|
|
549
|
-
let count = 0;
|
|
550
|
-
for (let lp of names) {
|
|
551
|
-
const digests = this._indexGet(lp, versionName);
|
|
552
|
-
if (digests) {
|
|
553
|
-
let c = this.delete(lp);
|
|
554
|
-
}
|
|
555
|
-
state[digests[this.digestAlgorithm]] = [lp];
|
|
556
|
-
this._indexAdd(lp, this.head, digests);
|
|
557
|
-
count++;
|
|
558
|
-
}
|
|
559
|
-
return count;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
module.exports = { OcflObjectInventory, OcflObjectInventoryMut, VersionNumber };
|