@ocfl/ocfl 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/index.js +131 -0
- package/index.mjs +18 -0
- package/lib/constants.js +24 -0
- package/lib/digest.js +138 -0
- package/lib/enum.js +92 -0
- package/lib/error.js +19 -0
- package/lib/extension.js +206 -0
- package/lib/extensions/0001-digest-algorithms.js +41 -0
- package/lib/extensions/0002-flat-direct-storage-layout.js +46 -0
- package/lib/extensions/0003-hash-and-id-n-tuple-storage-layout.js +58 -0
- package/lib/extensions/0004-hashed-n-tuple-storage-layout.js +61 -0
- package/lib/extensions/0005-mutable-head.js +0 -0
- package/lib/extensions/0006-flat-omit-prefix-storage-layout.js +7 -0
- package/lib/extensions/0007-n-tuple-omit-prefix-storage-layout.js +7 -0
- package/lib/extensions/0008-schema-registry.js +0 -0
- package/lib/extensions/index.js +12 -0
- package/lib/index.js +25 -0
- package/lib/inventory.js +393 -0
- package/lib/object.js +530 -0
- package/lib/storage.js +324 -0
- package/lib/store.js +153 -0
- package/lib/temp.js +264 -0
- package/lib/transaction.js +413 -0
- package/lib/types.d.ts +44 -0
- package/lib/utils.js +92 -0
- package/lib/validation.js +174 -0
- package/package.json +63 -0
- package/resources/ocfl_1.0.txt +936 -0
package/lib/storage.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
//@todo add default layout
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const validation = require('./validation.js');
|
|
5
|
+
const { NotImplementedError } = require("./error");
|
|
6
|
+
const { OcflStorageLayout } = require("./extension");
|
|
7
|
+
const { HashedNTupleStorageLayout } = require("./extensions/0004-hashed-n-tuple-storage-layout");
|
|
8
|
+
const { OcflObject, OcflObjectImpl } = require("./object");
|
|
9
|
+
const { isDirEmpty, findNamasteVersion } = require("./utils.js");
|
|
10
|
+
const { OCFL_VERSION, OCFL_VERSIONS, OCFL_LAYOUT,
|
|
11
|
+
EXTENSIONS_DIR, EXTENSION_CONFIG,
|
|
12
|
+
NAMASTE_PREFIX_STORAGE, NAMASTE_PREFIX_OBJECT, NAMASTE_T } = require('./constants').OcflConstants;
|
|
13
|
+
const DIGEST = require('./digest').OcflDigest.CONTENT;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const DEFAULT_LAYOUT = HashedNTupleStorageLayout;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {import('./extension').OcflExtensionConfig} OcflExtensionConfig
|
|
20
|
+
* @typedef {import('./store').OcflStore} OcflStore
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
class OcflStorage {
|
|
24
|
+
static LAYOUT;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the storage root
|
|
28
|
+
* @return {string}
|
|
29
|
+
*/
|
|
30
|
+
get root() { throw new NotImplementedError(); }
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Construct the path of the object root relative to the storage root
|
|
34
|
+
* The path is mapped from the object identifier using the algorithm defined in the chosen
|
|
35
|
+
* storage layout extension (specified in {@link OcflStorageConfig}).
|
|
36
|
+
* @param {string} id - Identifier of the object
|
|
37
|
+
* @return {string}
|
|
38
|
+
*/
|
|
39
|
+
objectRoot(id) { throw new NotImplementedError(); }
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Absolute path to the object root
|
|
43
|
+
* @param {string} id - Identifier of the object
|
|
44
|
+
*/
|
|
45
|
+
objectRootAbs(id) {
|
|
46
|
+
return path.join(this.root, this.objectRoot(id));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get an existing object in the Storage or create a new one.
|
|
51
|
+
* @param {string|{id?:string, root?:string}} opt - A unique identifier for the OCFL Object, should be a URI
|
|
52
|
+
* @return {OcflObject}
|
|
53
|
+
*/
|
|
54
|
+
object(opt) { throw new NotImplementedError(); }
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if the storage has already the object with the given id.
|
|
58
|
+
* @param {*} id
|
|
59
|
+
*/
|
|
60
|
+
async has(id) {
|
|
61
|
+
return this.object(id).exists();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create the storage root in the underlying storage backend
|
|
66
|
+
* @param {Object} [options] - Additional options for creating storage root
|
|
67
|
+
* @param {boolean} [options.ocflSpecText=false] - If true, create a copy of the OCFL specification text in the storage root
|
|
68
|
+
*/
|
|
69
|
+
async create(options) { throw new NotImplementedError(); }
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if the storage root path points to an existing file or non-empty directory in the underlying backend store.
|
|
73
|
+
* The existing directory may or may not be a valid OCFL Storage.
|
|
74
|
+
* @return {Promise<boolean>}
|
|
75
|
+
*/
|
|
76
|
+
async exists() { throw new NotImplementedError(); }
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check namaste, retrieve storage layout extension, and read its config file.
|
|
80
|
+
* @return {Promise<boolean>}
|
|
81
|
+
*/
|
|
82
|
+
async load() { throw new NotImplementedError(); }
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @return {AsyncIterator<OcflObject>}
|
|
87
|
+
*/
|
|
88
|
+
[Symbol.asyncIterator]() { throw new NotImplementedError(); }
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Return all OCFL objects under this storage as array
|
|
92
|
+
* @return {Promise<OcflObject[]>}
|
|
93
|
+
*/
|
|
94
|
+
async objects() {
|
|
95
|
+
const objects = [];
|
|
96
|
+
for await(const o of this) objects.push(o);
|
|
97
|
+
return objects;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async delete(id) {
|
|
101
|
+
return this.remove(id);
|
|
102
|
+
}
|
|
103
|
+
async purge(id) {
|
|
104
|
+
return this.remove(id);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async remove(id) { throw new NotImplementedError(); }
|
|
108
|
+
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @typedef {Object} OcflStorageConfig
|
|
113
|
+
* @property {string} root - Absolute path to the ocfl storage root
|
|
114
|
+
* @property {string} [workspace] - Absolute path to storage workspace directory
|
|
115
|
+
* @property {OcflStorageLayout|OcflExtensionConfig|string} [layout] - A layout that identifies an arrangement of directories and OCFL objects under the storage root
|
|
116
|
+
* @property {('sha256' | 'sha512')} [digestAlgorithm] - Digest algorithm for content-addressing, must use either sha512 or sha256
|
|
117
|
+
* @property {string} [contentDirectory='content'] - Content directory name. Only applies to a newly created object.
|
|
118
|
+
* @property {string} [ocflVersion=c.OCFL_VERSION] - Ocfl version. Only applies to a newly created object.
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* General implementation of {@link OcflStorage} that can uses different datastore backends.
|
|
123
|
+
* This class provides common functionalities for the subclasses and
|
|
124
|
+
* at the same provide encapsulation emulating private and protected methods.
|
|
125
|
+
* @implements {OcflStorage}
|
|
126
|
+
*/
|
|
127
|
+
class OcflStorageImpl extends OcflStorage {
|
|
128
|
+
|
|
129
|
+
/** @type {string} */
|
|
130
|
+
#root;
|
|
131
|
+
/** @type {string} */
|
|
132
|
+
#workspace;
|
|
133
|
+
/** @type {OcflStorageLayout} */
|
|
134
|
+
#layout;
|
|
135
|
+
/** @type {OcflStore} */
|
|
136
|
+
#store;
|
|
137
|
+
#objectConfig;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
* @param {OcflStorageConfig} config
|
|
142
|
+
* @param {OcflStore} store
|
|
143
|
+
*/
|
|
144
|
+
constructor(config, store) {
|
|
145
|
+
super();
|
|
146
|
+
if (!store) throw new TypeError('[OcflStorage] store is required.');
|
|
147
|
+
if (!config.root) throw new TypeError('[OcflStorage] config.root is required.');
|
|
148
|
+
this.#store = store;
|
|
149
|
+
const { root, workspace, layout, ocflVersion, ...objectConfig } = config;
|
|
150
|
+
this.#root = path.resolve(root);
|
|
151
|
+
this.#workspace = workspace ? path.resolve(workspace) : path.join(this.#root, 'extensions/workspace');
|
|
152
|
+
if (this.#workspace && this.#workspace.startsWith(this.#root + path.sep) && !this.#workspace.startsWith(this.#root + path.sep + 'extensions' + path.sep)) {
|
|
153
|
+
throw new Error('[OcflStorage] config.workspace cannot be the same as or a subpath of config.root');
|
|
154
|
+
}
|
|
155
|
+
this.#layout = layout instanceof OcflStorageLayout ? layout : this.#createLayout(layout);
|
|
156
|
+
this.ocflVersion = ocflVersion || OCFL_VERSION;
|
|
157
|
+
this.#objectConfig = objectConfig;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
*
|
|
162
|
+
* @param {OcflExtensionConfig|string} option
|
|
163
|
+
*/
|
|
164
|
+
#createLayout(option) {
|
|
165
|
+
let name = typeof option === 'string' ? option : option?.extensionName;
|
|
166
|
+
let config = typeof option === 'object' ? option : null;
|
|
167
|
+
if (name) {
|
|
168
|
+
return OcflStorageLayout.class(name).create(config);
|
|
169
|
+
}
|
|
170
|
+
// else if (useDefault) {
|
|
171
|
+
// return DEFAULT_LAYOUT.create(config);
|
|
172
|
+
// }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
get root() { return this.#root; }
|
|
176
|
+
|
|
177
|
+
objectRoot(id) {
|
|
178
|
+
if (!this.#layout) throw new Error('[OcflStorage] No layout defined');
|
|
179
|
+
return this.#layout.map(id);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The root param is a relative path to the storage root.
|
|
184
|
+
* @param {string|{id?:string, root?:string}} opt
|
|
185
|
+
*/
|
|
186
|
+
object(opt) {
|
|
187
|
+
let id, root;
|
|
188
|
+
if (typeof opt === 'string') id = opt;
|
|
189
|
+
else ({id, root} = opt);
|
|
190
|
+
let relObjectRoot = root || this.objectRoot(id);
|
|
191
|
+
return new OcflObjectImpl({
|
|
192
|
+
id,
|
|
193
|
+
root: path.join(this.root, relObjectRoot),
|
|
194
|
+
// @todo:use extension workspace if not defined
|
|
195
|
+
workspace: this.#workspace ? path.join(this.#workspace, relObjectRoot) : undefined,
|
|
196
|
+
ocflVersion: this.ocflVersion,
|
|
197
|
+
...this.#objectConfig
|
|
198
|
+
},
|
|
199
|
+
this.#store);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async exists() {
|
|
203
|
+
return !(await isDirEmpty(this.#store, this.root));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async load() {
|
|
207
|
+
let prefix = NAMASTE_PREFIX_STORAGE;
|
|
208
|
+
let tasks = [
|
|
209
|
+
findNamasteVersion(this.#store, prefix, this.root),
|
|
210
|
+
this.#store.readFile(path.join(this.root, OCFL_LAYOUT), 'utf8')
|
|
211
|
+
];
|
|
212
|
+
// @ts-ignore
|
|
213
|
+
let [ocflVersion, layout] = (await Promise.allSettled(tasks)).map(r => r.value);
|
|
214
|
+
if (!ocflVersion) throw new Error('Invalid storage root');//throw validation.createError(67, this.root);
|
|
215
|
+
let layoutName, layoutConfig;
|
|
216
|
+
// load layout
|
|
217
|
+
try {
|
|
218
|
+
if (layout) layoutName = JSON.parse(layout).extension;
|
|
219
|
+
} catch (error) {
|
|
220
|
+
}
|
|
221
|
+
// load ext config if exists
|
|
222
|
+
try {
|
|
223
|
+
let cfgstr = await this.#store.readFile(path.join(this.root, EXTENSIONS_DIR, layoutName, EXTENSION_CONFIG), 'utf8');
|
|
224
|
+
layoutConfig = JSON.parse(/**@type{string}*/(cfgstr));
|
|
225
|
+
} catch (error) {
|
|
226
|
+
}
|
|
227
|
+
this.#layout = this.#createLayout(layoutConfig || layoutName);
|
|
228
|
+
|
|
229
|
+
//@todo: load storage level extensions
|
|
230
|
+
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @param {Object} [options] - Additional options for creating storage root
|
|
236
|
+
* @param {boolean} [options.ocflSpecText=false] - If true, create a copy of the OCFL specification text in the storage root
|
|
237
|
+
*/
|
|
238
|
+
async create(options) {
|
|
239
|
+
if (await this.exists()) {
|
|
240
|
+
//cannot create, storage root already exists .
|
|
241
|
+
throw new Error('[OcflStorage] Cannot create storage root in a non-empty directory');
|
|
242
|
+
} else {
|
|
243
|
+
await this.#store.mkdir(this.root);
|
|
244
|
+
}
|
|
245
|
+
// create namaste
|
|
246
|
+
let prefix = NAMASTE_PREFIX_STORAGE;
|
|
247
|
+
let filenamePrefix = NAMASTE_T + prefix;
|
|
248
|
+
let filePath = path.join(this.root, filenamePrefix + this.ocflVersion);
|
|
249
|
+
await this.#store.writeFile(filePath, prefix + this.ocflVersion + '\n', 'utf8');
|
|
250
|
+
|
|
251
|
+
// create layout
|
|
252
|
+
if (!this.#layout) {
|
|
253
|
+
this.#layout = new DEFAULT_LAYOUT();
|
|
254
|
+
}
|
|
255
|
+
let layout = { extension: this.#layout.name, description: this.#layout.description };
|
|
256
|
+
await this.#store.writeFile(path.join(this.root, OCFL_LAYOUT), JSON.stringify(layout, null, 2), 'utf8');
|
|
257
|
+
if (this.#layout.config) {
|
|
258
|
+
await this.#store.writeFile(path.join(this.root, EXTENSIONS_DIR, this.#layout.name, EXTENSION_CONFIG), JSON.stringify(this.#layout.config, null, 2), 'utf8');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// todo: create other extensions
|
|
262
|
+
if (options?.ocflSpecText) {
|
|
263
|
+
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @return {AsyncIterator<OcflObject, OcflObject>}
|
|
269
|
+
*/
|
|
270
|
+
[Symbol.asyncIterator]() {
|
|
271
|
+
let store = this.#store;
|
|
272
|
+
let workspace = this.#workspace;
|
|
273
|
+
let config = this.#objectConfig;
|
|
274
|
+
let root = this.root;
|
|
275
|
+
let stack = [this.#store.opendir(this.root)];
|
|
276
|
+
return {
|
|
277
|
+
async next() {
|
|
278
|
+
let dirp;
|
|
279
|
+
while ((dirp = stack.pop())) {
|
|
280
|
+
let dir = await dirp;
|
|
281
|
+
let dirent = await dir.read();
|
|
282
|
+
if (!dirent) {
|
|
283
|
+
await dir.close();
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
stack.push(dirp);
|
|
287
|
+
if (!dirent.isDirectory()) continue;
|
|
288
|
+
let basePath = path.join(dir.path, dirent.name);
|
|
289
|
+
let nv;
|
|
290
|
+
try {
|
|
291
|
+
nv = await findNamasteVersion(store, NAMASTE_PREFIX_OBJECT, basePath);
|
|
292
|
+
} catch (err) {
|
|
293
|
+
}
|
|
294
|
+
if (nv) {
|
|
295
|
+
//ocfl object exists
|
|
296
|
+
// inventory exists
|
|
297
|
+
let object = new OcflObjectImpl({
|
|
298
|
+
root: basePath,
|
|
299
|
+
workspace: workspace ? path.join(workspace, path.relative(root, basePath)) : undefined,
|
|
300
|
+
ocflVersion: nv,
|
|
301
|
+
...config
|
|
302
|
+
}, store);
|
|
303
|
+
//await object.load();
|
|
304
|
+
return { done: false, value: object };
|
|
305
|
+
} else {
|
|
306
|
+
//ocfl object does not exist, process subdir
|
|
307
|
+
stack.push(store.opendir(basePath));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
}
|
|
311
|
+
return { done: true, value: null };
|
|
312
|
+
}
|
|
313
|
+
// return() {
|
|
314
|
+
// return { done: true };
|
|
315
|
+
// }
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
module.exports = {
|
|
322
|
+
OcflStorage,
|
|
323
|
+
OcflStorageImpl
|
|
324
|
+
};
|
package/lib/store.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { pipeline } = require('stream/promises');
|
|
3
|
+
const { parallelize, dataSourceAsIterable } = require('./utils');
|
|
4
|
+
|
|
5
|
+
const emptyOptions = {};
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Abstract class to provide common APIs to access and modify the actual data store backend
|
|
9
|
+
* such as local file system and cloud storage
|
|
10
|
+
*/
|
|
11
|
+
class OcflStore {
|
|
12
|
+
static instances;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} [options]
|
|
17
|
+
*/
|
|
18
|
+
static getInstance(options = emptyOptions) {
|
|
19
|
+
if (!this.instances) return new this(options);
|
|
20
|
+
let store = this.instances.get(options);
|
|
21
|
+
if (!store) {
|
|
22
|
+
store = new this(options);
|
|
23
|
+
this.instances.set(options, store);
|
|
24
|
+
}
|
|
25
|
+
return store;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
constructor(options) { }
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get file information.
|
|
32
|
+
* @param {string} filePath
|
|
33
|
+
* @return {Promise<import('fs').Stats>}
|
|
34
|
+
*/
|
|
35
|
+
async stat(filePath) { throw new Error('Not Implemented'); }
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if file exists
|
|
39
|
+
* @param {string} filePath
|
|
40
|
+
*/
|
|
41
|
+
async exists(filePath) {
|
|
42
|
+
try {
|
|
43
|
+
await this.stat(filePath);
|
|
44
|
+
return true;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a readable stream to get the content of a file
|
|
52
|
+
* @param {string} filePath - Absolute path
|
|
53
|
+
* @param {*} options
|
|
54
|
+
* @return {Promise<import('fs').ReadStream>}
|
|
55
|
+
*/
|
|
56
|
+
async createReadStream(filePath, options) { throw new Error('Not Implemented'); }
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create a stream that writes data to {@link relPath}.
|
|
60
|
+
* @param {string} relPath
|
|
61
|
+
* @param {*} options
|
|
62
|
+
* @return {Promise<import('fs').WriteStream>}
|
|
63
|
+
*/
|
|
64
|
+
async createWriteStream(relPath, options) { throw new Error('Not Implemented'); }
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Provide a common interface to read a file inside an object.
|
|
68
|
+
* A concrete subclass MAY implement this method to provide read access to its underlying storage backend.
|
|
69
|
+
* The default implementation uses the {@linkcode OcflStore#createReadStream} method.
|
|
70
|
+
* @param {string} filePath
|
|
71
|
+
* @param {*} [options]
|
|
72
|
+
*/
|
|
73
|
+
async readFile(filePath, options) {
|
|
74
|
+
let rs = await this.createReadStream(filePath, options);
|
|
75
|
+
let chunks = [];
|
|
76
|
+
for await (const chunk of rs) {
|
|
77
|
+
chunks.push(chunk);
|
|
78
|
+
}
|
|
79
|
+
if (typeof options === 'string' || options.encoding) {
|
|
80
|
+
return Buffer.concat(chunks);
|
|
81
|
+
} else {
|
|
82
|
+
return chunks.join('');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @typedef {string|Buffer|NodeJS.ArrayBufferView|AsyncIterable|Iterable|import('stream').Readable} WriteFileData */
|
|
87
|
+
/**
|
|
88
|
+
* Provide a common interface to write a file inside an object.
|
|
89
|
+
* A concrete subclass MAY implement this method to provide write access to its underlying storage backend.
|
|
90
|
+
* The default implementation uses the {@link OcflStore.createWriteStream} method.
|
|
91
|
+
* @param {string} filePath - Absolute path to the file.
|
|
92
|
+
* @param {WriteFileData} data - The data to be written to the file.
|
|
93
|
+
* @param {Object|string} options
|
|
94
|
+
*/
|
|
95
|
+
async writeFile(filePath, data, options) {
|
|
96
|
+
var source = dataSourceAsIterable(data);
|
|
97
|
+
await this.mkdir(path.dirname(filePath));
|
|
98
|
+
const target = await this.createWriteStream(filePath, options);
|
|
99
|
+
await pipeline(source, target);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Copy file from `source` to `target`.
|
|
104
|
+
* By default, target is overwritten if it already exists.
|
|
105
|
+
* @param {string} source - source filename to copy
|
|
106
|
+
* @param {string} target - destination filename of the copy operation
|
|
107
|
+
*/
|
|
108
|
+
async copyFile(source, target) {
|
|
109
|
+
await this.writeFile(target, await this.createReadStream(source));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Open a directory
|
|
114
|
+
* @param {string} filePath
|
|
115
|
+
* @param {*} options
|
|
116
|
+
* @return {Promise<import('fs').Dir> }
|
|
117
|
+
*/
|
|
118
|
+
async opendir(filePath, options) { throw new Error('Not Implemented'); }
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Read a directory
|
|
122
|
+
* @param {string} filePath
|
|
123
|
+
* @param {*} options
|
|
124
|
+
* @return {Promise<string[]> }
|
|
125
|
+
*/
|
|
126
|
+
async readdir(filePath, options) { throw new Error('Not Implemented'); }
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Recursively create a directory if the storage backend supports it, otherwise do nothing
|
|
130
|
+
* @param {string} filePath - The directory path
|
|
131
|
+
* @param {*} [options] - Options to be passed to the underlying method
|
|
132
|
+
* @return {Promise<string>} - The first directory path created
|
|
133
|
+
*/
|
|
134
|
+
async mkdir(filePath, options) { throw new Error('Not Implemented'); }
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Rename or move a file or directory
|
|
138
|
+
* @param {string} source
|
|
139
|
+
* @param {string} target
|
|
140
|
+
*/
|
|
141
|
+
async move(source, target) { throw new Error('Not Implemented'); }
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Remove a file or directory recursively
|
|
145
|
+
* @param {*} filePath
|
|
146
|
+
*/
|
|
147
|
+
async remove(filePath) { throw new Error('Not Implemented'); }
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
OcflStore
|
|
153
|
+
};
|