@ocfl/ocfl 0.4.0 → 1.0.0-beta.2
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 +82 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +169 -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 +394 -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 -51
- package/src/constants.ts +16 -0
- package/src/digest.ts +258 -0
- package/src/error.ts +9 -0
- package/src/extension.ts +204 -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 +455 -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 -147
- package/lib/index.js +0 -13
- package/lib/inventory.js +0 -564
- package/lib/object.js +0 -499
- package/lib/storage.js +0 -338
- package/lib/store.js +0 -191
- package/lib/transaction.js +0 -448
- package/lib/utils.js +0 -122
- package/lib/validation.js +0 -174
package/lib/object.js
DELETED
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const validation = require('./validation.js');
|
|
6
|
-
const { enumeration } = require('./enum.js');
|
|
7
|
-
const { OcflObjectInventory, OcflObjectInventoryMut, VersionNumber } = require('./inventory.js');
|
|
8
|
-
const { OcflDigest } = require('./digest.js');
|
|
9
|
-
const { OcflObjectTransaction, OcflObjectTransactionImpl } = require('./transaction.js');
|
|
10
|
-
const { OcflStore } = require('./store.js');
|
|
11
|
-
const { OcflExtension } = require('./extension.js');
|
|
12
|
-
const { NotImplementedError } = require('./error.js');
|
|
13
|
-
const { parallelize, isDirEmpty, findNamasteVersion } = require('./utils');
|
|
14
|
-
const { OcflObjectFile } = require('./file.js');
|
|
15
|
-
const { OCFL_VERSION, OCFL_VERSIONS, INVENTORY_NAME, NAMASTE_PREFIX_OBJECT, NAMASTE_T } = require('./constants').OcflConstants;
|
|
16
|
-
|
|
17
|
-
const UPDATE_MODES = /** @type { const } */(['MERGE', 'REPLACE']);
|
|
18
|
-
const UPDATE_MODE = enumeration(UPDATE_MODES);
|
|
19
|
-
const DIGEST = OcflDigest.CONTENT;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @typedef {(typeof UPDATE_MODES)[number]} UpdateModeStr
|
|
23
|
-
* @typedef { InstanceType<typeof UPDATE_MODE> | UpdateModeStr} UpdateMode
|
|
24
|
-
* @typedef {string} LogicalPath
|
|
25
|
-
* @typedef {{logicalPath: string, version?: string}} FileRefLogical
|
|
26
|
-
* @typedef {{digest: string, version?: string}} FileRefDigest
|
|
27
|
-
* @typedef {{contentPath: string, version?: string}} FileRefContent
|
|
28
|
-
* @typedef {import('./types').Inventory} Inventory
|
|
29
|
-
* @typedef {import('./types').OcflVersion} OcflVersion
|
|
30
|
-
* @typedef {import('./types').OcflObjectConfig} OcflObjectConfig
|
|
31
|
-
*
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* A class representing an OCFL Object allowing to read and write the content of an OCFL Object in the underlying store.
|
|
36
|
-
*/
|
|
37
|
-
class OcflObject {
|
|
38
|
-
|
|
39
|
-
/** @type {OcflVersion} */
|
|
40
|
-
#ocflVersion;
|
|
41
|
-
/** @type {string} */
|
|
42
|
-
#root;
|
|
43
|
-
/** @type {string} */
|
|
44
|
-
#workspace;
|
|
45
|
-
/** @type {string} */
|
|
46
|
-
#id;
|
|
47
|
-
/** @type {('sha256' | 'sha512')} */
|
|
48
|
-
#digestAlgorithm;
|
|
49
|
-
/** @type {OcflExtension[]} */
|
|
50
|
-
#extensions;
|
|
51
|
-
/** @type {OcflStore} */
|
|
52
|
-
#store;
|
|
53
|
-
/**
|
|
54
|
-
* A default list of digest algorithms to be calculated for each file in this object and added to the fixity block.
|
|
55
|
-
* @type {string[]}
|
|
56
|
-
*/
|
|
57
|
-
fixityAlgorithms;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Create a new OCFL Object
|
|
61
|
-
* @param {OcflObjectConfig} config
|
|
62
|
-
* @param {OcflStore} store
|
|
63
|
-
*/
|
|
64
|
-
constructor(config, store) {
|
|
65
|
-
if (!store) throw new TypeError('[OcflObject] store is required.');
|
|
66
|
-
this.#store = store;
|
|
67
|
-
if (!config.root) throw new TypeError('[OcflObject] config.root is required.');
|
|
68
|
-
if (config.root === config.workspace) throw new Error('[OcflObject] config.root and config.workspace must be different.');
|
|
69
|
-
this.#root = path.resolve(config.root);
|
|
70
|
-
this.#ocflVersion = config.ocflVersion || /** @type {OcflVersion} */(OCFL_VERSION);
|
|
71
|
-
//this.contentVersion = null; // No content yet
|
|
72
|
-
this.#id = config.id;
|
|
73
|
-
this.#extensions = config.extensions;
|
|
74
|
-
//this.fixityDigest = config.fixityDigest ?? this.digestAlgorithm;
|
|
75
|
-
this.digestAlgorithm = config.digestAlgorithm || 'sha512';
|
|
76
|
-
this.#workspace = config.workspace ? path.resolve(config.workspace) : undefined;
|
|
77
|
-
this.contentDirectory = config.contentDirectory ?? 'content';
|
|
78
|
-
/** @type Object.<string, OcflObjectInventory> */
|
|
79
|
-
this._inventory = {};
|
|
80
|
-
this.fixityAlgorithms = config.fixityAlgorithms;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** the digest algorithm used to identify files within the OCFL object */
|
|
84
|
-
get digestAlgorithm() { return this.#digestAlgorithm; }
|
|
85
|
-
set digestAlgorithm(algorithm) {
|
|
86
|
-
this.#digestAlgorithm = algorithm;
|
|
87
|
-
// @ts-ignore
|
|
88
|
-
this.#digestAlgorithm = enumeration.of(DIGEST, algorithm || 'sha512')?.name;
|
|
89
|
-
if (!this.#digestAlgorithm) throw new Error('Invalid digest algorithm. Must be one of `sha256` or `sha512`.');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Identifier of the OCFL Object
|
|
94
|
-
* @return {string}
|
|
95
|
-
*/
|
|
96
|
-
get id() { return this.inventory?.id || this.#id; }
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Latest version number of the OCFL Object
|
|
100
|
-
* @return {VersionNumber}
|
|
101
|
-
*/
|
|
102
|
-
get headVersion() {
|
|
103
|
-
const head = this.inventory?.head;
|
|
104
|
-
if (head) return VersionNumber.fromString(head);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* All available version numbers in the OCFL Object
|
|
109
|
-
* @return {VersionNumber[]}
|
|
110
|
-
*/
|
|
111
|
-
get versions() {
|
|
112
|
-
return Object.keys(this.inventory?.versions || {}).map(v => VersionNumber.fromString(v));
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* The absolute path to the root of this OCFL Object
|
|
117
|
-
* @return {string}
|
|
118
|
-
*/
|
|
119
|
-
get root() { return this.#root; }
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* The absolute path to the temporary workspace of this OCFL Object
|
|
123
|
-
* @return {string}
|
|
124
|
-
*/
|
|
125
|
-
get workspace() { return this.#workspace; }
|
|
126
|
-
|
|
127
|
-
/** The raw OCFL version number the object adheres to , eg 1.0, 1.1) */
|
|
128
|
-
get ocflVersion() { return this.#ocflVersion; }
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Create a new backend-specific update transaction
|
|
132
|
-
* @param {OcflObjectInventoryMut} inventory
|
|
133
|
-
* @return {Promise<OcflObjectTransaction>}
|
|
134
|
-
*/
|
|
135
|
-
async #createTransaction(inventory) {
|
|
136
|
-
await this._validateObjectPath();
|
|
137
|
-
if (!this.workspace) await this._ensureNamaste();
|
|
138
|
-
let workspacePath = this.workspace || this.root;
|
|
139
|
-
return OcflObjectTransactionImpl.create(this, this.#store, inventory, workspacePath);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async _ensureNamaste() {
|
|
143
|
-
let prefix = NAMASTE_PREFIX_OBJECT;
|
|
144
|
-
let nv = await findNamasteVersion(this.#store, prefix, this.root);
|
|
145
|
-
if (nv === '') throw validation.createError(6, this.root);
|
|
146
|
-
if (nv) {
|
|
147
|
-
if (!this.ocflVersion) this.#ocflVersion = /** @type {OcflVersion} */(nv);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
// namaste not found, check if object root is not empty
|
|
151
|
-
let files;
|
|
152
|
-
try {
|
|
153
|
-
files = await this.#store.readdir(this.root);
|
|
154
|
-
} catch (error) {
|
|
155
|
-
}
|
|
156
|
-
if (files && files.length > 0) throw new Error('Cannot create an OCFL Object in a non-empty directory');
|
|
157
|
-
//create the namaste file
|
|
158
|
-
let filePath = path.join(this.root, NAMASTE_T + prefix + this.ocflVersion);
|
|
159
|
-
await this.#store.writeFile(filePath, prefix + this.ocflVersion + '\n', 'utf8');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Read and parse the inventory.json file
|
|
164
|
-
* @param {string} [version] - Full name of the version, eg: v1, v2, v3
|
|
165
|
-
* @returns {Promise<OcflObjectInventory>}
|
|
166
|
-
*/
|
|
167
|
-
async _readInventory(version = 'latest') {
|
|
168
|
-
let invPath = version === 'latest' ? INVENTORY_NAME : path.join(version, INVENTORY_NAME);
|
|
169
|
-
let datastr;
|
|
170
|
-
try {
|
|
171
|
-
datastr = /** @type string */(await this.readFile(invPath, 'utf8'));
|
|
172
|
-
} catch (err) {
|
|
173
|
-
if (err.code !== 'ENOENT') throw err;
|
|
174
|
-
//inventory does not exist yet
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
let data = /** @type Inventory */(JSON.parse(datastr));
|
|
178
|
-
let [digest, actualDigest] = /** @type [string,string] */ (await Promise.all([
|
|
179
|
-
this.readFile(invPath + '.' + data.digestAlgorithm, 'utf8'),
|
|
180
|
-
OcflDigest.digest(data.digestAlgorithm, datastr)
|
|
181
|
-
]));
|
|
182
|
-
digest = digest.match(/[^\s]+/)?.[0];
|
|
183
|
-
if (digest !== actualDigest) throw new Error(`Inventory file ${this.root}/${invPath} digest mismatch: recorded=${digest} actual=${actualDigest}`);
|
|
184
|
-
//return new OcflObjectInventory({ data, digest });
|
|
185
|
-
return new OcflObjectInventory(data);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Set the latest version of inventory in the cache
|
|
190
|
-
* @param {*} inventory
|
|
191
|
-
* @param {*} version
|
|
192
|
-
*/
|
|
193
|
-
_setInventory(inventory, version = 'latest') {
|
|
194
|
-
//this._inventory['latest'] = this._inventory[inventory.head] = inventory;
|
|
195
|
-
this._inventory[version] = inventory;
|
|
196
|
-
if (version === 'latest') this._inventory[inventory.head] = inventory;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// all the parent directories cannot be an ocfl object
|
|
200
|
-
async _validateObjectPath() {
|
|
201
|
-
var p = this.root;
|
|
202
|
-
while (p !== '.' && p !== '/') {
|
|
203
|
-
p = path.dirname(p);
|
|
204
|
-
let nv = await findNamasteVersion(this.#store, NAMASTE_PREFIX_OBJECT, p);
|
|
205
|
-
if (nv) throw new Error(`Object root cannot be nested under another object ${p}`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* If a cache exists, return the cached inventory. Otherwise read it from the storage and cache it.
|
|
211
|
-
* If the inventory file does not exist, eg in a newly created object, returns undefined.
|
|
212
|
-
* @param {string} [version] - Full name of the version, eg: v1, v2, v3
|
|
213
|
-
* @returns {Promise<OcflObjectInventory>}
|
|
214
|
-
*/
|
|
215
|
-
async getInventory(version = 'latest') {
|
|
216
|
-
if (!this._inventory[version]) {
|
|
217
|
-
let inv = await this._readInventory(version);
|
|
218
|
-
if (inv) this._setInventory(inv, version);
|
|
219
|
-
}
|
|
220
|
-
return this._inventory[version];
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
_baseInventory() {
|
|
224
|
-
return {
|
|
225
|
-
id: this.id,
|
|
226
|
-
digestAlgorithm: this.digestAlgorithm || 'sha512',
|
|
227
|
-
type: /** @type {Inventory["type"]} */(`https://ocfl.io/${this.ocflVersion}/spec/#inventory`),
|
|
228
|
-
contentDirectory: this.contentDirectory || 'content'
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Update the content files or directories as one transaction and commit the changes as a new version.
|
|
234
|
-
* After obtaining the OcflObjectTransaction instance, make sure to either call the
|
|
235
|
-
* {@link OcflObjectTransaction#commit} or {@link OcflObjectTransaction#rollback} method to complete the transaction.
|
|
236
|
-
* If callback function `cb` is provided, all update operations can be done in the callback function
|
|
237
|
-
* and all changes are automatically commited at the end of the function.
|
|
238
|
-
* @param {function(OcflObjectTransaction):Promise<*>} [updater]
|
|
239
|
-
* @param {UpdateMode} [mode_]
|
|
240
|
-
* @return {Promise<?OcflObjectTransaction>}
|
|
241
|
-
*/
|
|
242
|
-
async update(updater, mode_ = UPDATE_MODE.MERGE) {
|
|
243
|
-
const mode = enumeration.of(UPDATE_MODE, mode_);
|
|
244
|
-
if (!mode) throw new TypeError(`Invalid mode '${mode.toString()}'`);
|
|
245
|
-
const inv = await this.getInventory() ?? this._baseInventory();
|
|
246
|
-
const newInv = await OcflObjectInventory.newVersion(inv, mode === UPDATE_MODE.REPLACE);
|
|
247
|
-
const t = await this.#createTransaction(newInv);
|
|
248
|
-
if (typeof updater === 'function') {
|
|
249
|
-
try {
|
|
250
|
-
await updater(t);
|
|
251
|
-
} catch (error) {
|
|
252
|
-
// console.log(error);
|
|
253
|
-
await t.rollback();
|
|
254
|
-
throw error;
|
|
255
|
-
}
|
|
256
|
-
await t.commit();
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
return t;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Import one or more content files or directories to the object
|
|
265
|
-
* in one transaction and commit the changes as a new version.
|
|
266
|
-
* Use this method to simplify copying all contents of a directory
|
|
267
|
-
* in local filesystem to the OCFL Object content directory
|
|
268
|
-
* @param {string|string[]|string[][]} content - The source path(s) or an array of a tuple [source, logical path]
|
|
269
|
-
* @param {UpdateMode} [mode=UPDATE_MODE.MERGE] - Update mode.
|
|
270
|
-
*/
|
|
271
|
-
async import(content, mode = UPDATE_MODE.MERGE) {
|
|
272
|
-
//let entries = /**@type {string[][]}*/(content);
|
|
273
|
-
if (typeof content === 'string') content = [[content, '']];
|
|
274
|
-
//else if (typeof content[0] === 'string') entries = /**@type {string[]}*/(content).map(src => [src, '']);
|
|
275
|
-
|
|
276
|
-
await this.update(async (t) => {
|
|
277
|
-
let res = await parallelize(/**@type {string[][]}*/(content), async (p) => {
|
|
278
|
-
if (typeof p === 'string') p = [p, ''];
|
|
279
|
-
let [source, target] = p;
|
|
280
|
-
return t.import(source, target);
|
|
281
|
-
});
|
|
282
|
-
let errors = [];
|
|
283
|
-
for (let i = 0; i < res.length; ++i) {
|
|
284
|
-
if (res[i] instanceof Error) errors.push(content[i][0]);
|
|
285
|
-
}
|
|
286
|
-
if (errors.length) {
|
|
287
|
-
let msg = "Cannot add the files: " + errors.join(', ');
|
|
288
|
-
throw new Error(msg);
|
|
289
|
-
}
|
|
290
|
-
}, mode);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Alias for import
|
|
295
|
-
* @see {@link OcflObject.import}
|
|
296
|
-
*/
|
|
297
|
-
async add(sourceDir, mode = UPDATE_MODE.MERGE) {
|
|
298
|
-
await this.import(sourceDir, mode);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Count the number of files contained in this object
|
|
303
|
-
* @param {string} [version]
|
|
304
|
-
*/
|
|
305
|
-
async count(version) {
|
|
306
|
-
let inv = await this.getInventory();
|
|
307
|
-
return [...inv.files(version)].length;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
async createReadable(filePath, options) {
|
|
311
|
-
return this.#store.createReadable(path.join(this.root, filePath), options);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Check if the object root path points to an existing file or non-empty directory
|
|
316
|
-
* in the underlying backend store.
|
|
317
|
-
* The existing directory may or may not be a valid OCFL Object.
|
|
318
|
-
* @return {Promise<boolean>}
|
|
319
|
-
*/
|
|
320
|
-
async exists() {
|
|
321
|
-
return !(await isDirEmpty(this.#store, this.root));
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Copy all content of this Object to a directory in local filesystem
|
|
326
|
-
* @param {string} targetDir
|
|
327
|
-
* @param {string} version
|
|
328
|
-
*/
|
|
329
|
-
async export(targetDir, version) { throw new Error('Not Implemented'); }
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Iterate through the content files contained in the specified version.
|
|
333
|
-
* Returns an Iterator that contains FileRef which consists of logical path, content path, and digest, in no particular order.
|
|
334
|
-
* The iterator is implemented as a generator.
|
|
335
|
-
* @param {string} [version]
|
|
336
|
-
* @return {Promise<Generator<OcflObjectFile, void, unknown>>}
|
|
337
|
-
*/
|
|
338
|
-
async files(version) {
|
|
339
|
-
let inv = await this.getInventory();
|
|
340
|
-
let o = this;
|
|
341
|
-
function* files() {
|
|
342
|
-
for (const f of inv.files(version)) {
|
|
343
|
-
yield new OcflObjectFile(o, f);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return files();
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Return a file representation by the content path and version,
|
|
351
|
-
* @param { string } contentPath
|
|
352
|
-
* @param { string } [version='latest']
|
|
353
|
-
*/
|
|
354
|
-
async fileByContentPath(contentPath, version) {
|
|
355
|
-
const inv = await this.getInventory();
|
|
356
|
-
const fr = inv?.getFileByContentPath(contentPath, version);
|
|
357
|
-
if (fr) return new OcflObjectFile(this, fr);
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Return a file representation by the digest and version,
|
|
362
|
-
* @param { string } digest
|
|
363
|
-
* @param { string } [version='latest']
|
|
364
|
-
*/
|
|
365
|
-
async fileByDigest(digest, version) {
|
|
366
|
-
const inv = await this.getInventory();
|
|
367
|
-
const fr = inv?.getFileByDigest(digest, version);
|
|
368
|
-
if (fr) return new OcflObjectFile(this, fr);
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Return a file representation by the logical path and version,
|
|
373
|
-
* @param { string } logicalPath
|
|
374
|
-
* @param { string } [version='latest']
|
|
375
|
-
*/
|
|
376
|
-
async fileByLogicalPath(logicalPath, version) {
|
|
377
|
-
const inv = await this.getInventory();
|
|
378
|
-
const fr = inv?.getFile(logicalPath, version);
|
|
379
|
-
if (fr) return new OcflObjectFile(this, fr);
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Return a file representation by either the [logical path and version], digest, or content path.
|
|
384
|
-
* The returned file object can be used to retrieve the content of a file either as string, buffer, or stream.
|
|
385
|
-
* If the object inventory file is not loaded, this file object will not include the full file metadata information.
|
|
386
|
-
* If version is omitted, it defaults to the last version.
|
|
387
|
-
* @param { LogicalPath | FileRefLogical | FileRefDigest | FileRefContent } opt A choice of logical path (eg: 'test') and version (eg: 'v1'),
|
|
388
|
-
* digest of the file content, or content path (eg: 'v1/content/test')
|
|
389
|
-
* @param {string} [version='latest'] The object version name, eg: 'v1', 'v2'. Default to 'latest'. Only used if opt is a LogicalPath.
|
|
390
|
-
* @param {string} [lazy] If true, always return a file instance even if the object inventory file is not loaded.
|
|
391
|
-
*/
|
|
392
|
-
getFile(opt, version, lazy) {
|
|
393
|
-
const inv = this.inventory;
|
|
394
|
-
/** @type {any} */
|
|
395
|
-
const file = typeof opt === 'string' ? { logicalPath: opt, version: version } : { ...opt };
|
|
396
|
-
if (inv) {
|
|
397
|
-
let fr;
|
|
398
|
-
if (file.logicalPath) {
|
|
399
|
-
fr = inv?.getFile(file.logicalPath, file.version);
|
|
400
|
-
} else if (file.digest) {
|
|
401
|
-
fr = inv?.getFileByDigest(file.digest, file.version);
|
|
402
|
-
} else if (file.contentPath) {
|
|
403
|
-
fr = inv?.getFileByContentPath(file.contentPath, file.version);
|
|
404
|
-
}
|
|
405
|
-
if (fr) return new OcflObjectFile(this, fr);
|
|
406
|
-
} else if (lazy) {
|
|
407
|
-
return new OcflObjectFile(this, file);
|
|
408
|
-
}
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Return the latest version of the cached data from inventory.json which can be read using {@link getInventory()}
|
|
413
|
-
*/
|
|
414
|
-
get inventory() {
|
|
415
|
-
return this._inventory.latest;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Reads the object inventory file and cache it in the memory.
|
|
420
|
-
* Optionally, preloads and reads all files metadata from the underlying storage.
|
|
421
|
-
* @param {object} [options]
|
|
422
|
-
* @param {boolean} [options.filesMetadata] Whether to read file metadata from the underlying storage.
|
|
423
|
-
*/
|
|
424
|
-
async load({ filesMetadata } = {}) {
|
|
425
|
-
this._inventory = {}; // reset inventory cache
|
|
426
|
-
const inv = await this.getInventory();
|
|
427
|
-
//await this.loadExtensions();
|
|
428
|
-
if (inv && filesMetadata) {
|
|
429
|
-
//load file metadata from underlying storage
|
|
430
|
-
const metadata = inv.metadata = {};
|
|
431
|
-
for await (const file of await this.#store.list(this.root, { recursive: true })) {
|
|
432
|
-
const contentPath = file.path;
|
|
433
|
-
metadata[contentPath] = {
|
|
434
|
-
size: file.size,
|
|
435
|
-
lastModified: file.lastModified.getTime()
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Read the content of a file inside an object.
|
|
443
|
-
* @param {string} relPath - The file path relative to the object root.
|
|
444
|
-
* @param {*} options - Options to be passed to the underlying method
|
|
445
|
-
*/
|
|
446
|
-
async readFile(relPath, options) {
|
|
447
|
-
return this.#store.readFile(path.join(this.root, relPath), options);
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Get the file metadata such as size and modification time.
|
|
452
|
-
* @param {string} relPath - The file path relative to the object root.
|
|
453
|
-
*/
|
|
454
|
-
async stat(relPath) {
|
|
455
|
-
return this.#store.stat(path.join(this.root, relPath));
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
toString() { return `OcflObject { root: ${this.root}, id: ${this.id} }`; }
|
|
459
|
-
|
|
460
|
-
toJSON() { return { root: this.root, id: this.id }; }
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* @implements {OcflObject}
|
|
468
|
-
*/
|
|
469
|
-
class OcflObjectImpl extends OcflObject {
|
|
470
|
-
|
|
471
|
-
// async _resolveContentPath({ logicalPath = '', contentPath = '', digest = '', version = '' }) {
|
|
472
|
-
// if (!contentPath) {
|
|
473
|
-
// let inv = await this.getInventory();
|
|
474
|
-
// version = version && version !== 'latest' ? version : inv.head;
|
|
475
|
-
// contentPath = inv.getContentPath(digest || inv.getDigest(logicalPath, version));
|
|
476
|
-
// }
|
|
477
|
-
// return contentPath;
|
|
478
|
-
// }
|
|
479
|
-
|
|
480
|
-
async isObject(aPath) { }
|
|
481
|
-
static status(rootPath) { }
|
|
482
|
-
static exists(rootPath) { }
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* Validate this object
|
|
486
|
-
* @abstract
|
|
487
|
-
*/
|
|
488
|
-
async validate() { throw new Error('Not Implemented'); }
|
|
489
|
-
async verify() { return this.validate(); }
|
|
490
|
-
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
module.exports = {
|
|
495
|
-
OcflObject,
|
|
496
|
-
OcflObjectInventory,
|
|
497
|
-
//OcflObjectTransaction,
|
|
498
|
-
OcflObjectTransactionImpl
|
|
499
|
-
};
|