@ocfl/ocfl 0.1.0 → 0.1.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/index.js CHANGED
@@ -108,8 +108,19 @@ class Ocfl {
108
108
  storage(config, storeOptions = this.#defaultOptions) {
109
109
  return new OcflStorageImpl(config, this.#store.getInstance(storeOptions));
110
110
  }
111
+
111
112
  }
112
113
 
114
+ class Storage {
115
+ async create(config) {
116
+
117
+ }
118
+ async load(config) {
119
+
120
+ }
121
+ }
122
+
123
+
113
124
  module.exports = {
114
125
  OcflConstants,
115
126
  OcflDigest,
@@ -28,8 +28,8 @@ class HashAndIdNTupleStorageLayout extends OcflStorageLayout {
28
28
  /**
29
29
  * @param {Partial<typeof DefaultConfig>} [config]
30
30
  */
31
- constructor(config) {
32
- super(config);
31
+ constructor(config) {
32
+ super(config, DefaultConfig);
33
33
  let c = this.parameters;
34
34
  if (!OcflDigest.FIXITY.of(c.digestAlgorithm)) throw new Error('Invalid digestAlgorithm');
35
35
  let digest = OcflDigest.digestSync(c.digestAlgorithm, 'test');
@@ -42,10 +42,10 @@ class HashAndIdNTupleStorageLayout extends OcflStorageLayout {
42
42
  * @return {string}
43
43
  */
44
44
  map(id) {
45
- let digest = OcflDigest.digestSync(this.config.digestAlgorithm, id);
45
+ let digest = OcflDigest.digestSync(this.parameters.digestAlgorithm, id);
46
46
  let segments = [];
47
- let s = this.config.tupleSize;
48
- let n = this.config.numberOfTuples;
47
+ let s = this.parameters.tupleSize;
48
+ let n = this.parameters.numberOfTuples;
49
49
  let i;
50
50
  for (i = 0; i < n; ++i) {
51
51
  segments.push(digest.slice(s * i, s * i + s));
package/lib/inventory.js CHANGED
@@ -155,13 +155,20 @@ class OcflObjectInventory {
155
155
  * @param {string} [version] - The version name
156
156
  */
157
157
  *files(version) {
158
- let state = (!version || version === 'latest') ? this.state : this.versions[version]?.state;
158
+ let state;
159
+ if (!version || version === 'latest') {
160
+ state = this.state;
161
+ version = this.head;
162
+ } else {
163
+ state = this.versions[version]?.state;
164
+ }
159
165
  for (let digest in state) {
160
166
  let files = state[digest];
161
167
  let contentPath = this.getContentPath(digest);
162
168
  for (let logicalPath of files) {
163
169
  yield {
164
170
  logicalPath,
171
+ version,
165
172
  digest,
166
173
  contentPath
167
174
  };
@@ -283,14 +290,13 @@ class OcflObjectInventoryMut extends OcflObjectInventory {
283
290
  if (mf && mf.length > 0) {
284
291
  // this is a reinstatement
285
292
  } else {
286
- if (this.#byPath[logicalPath]) {
287
- // this is an update, remove old digest
288
- this.delete(logicalPath);
289
- } else {
290
- // normal addition
291
- }
293
+ // normal addition
292
294
  this.manifest[digest] = [path.join(this.head, this.contentDirectory, logicalPath)];
293
295
  }
296
+ if (this.#byPath[logicalPath]) {
297
+ // this is an update, remove old digest
298
+ this.delete(logicalPath);
299
+ }
294
300
  state[digest] = [logicalPath];
295
301
  }
296
302
  this.#byPath[logicalPath] = digest;
@@ -350,6 +356,10 @@ class OcflObjectInventoryMut extends OcflObjectInventory {
350
356
  }
351
357
  }
352
358
 
359
+ /**
360
+ * Delete a file
361
+ * @param {string} logicalPath
362
+ */
353
363
  delete(logicalPath) {
354
364
  let prefix = logicalPath.endsWith('/') ? logicalPath : logicalPath + '/';
355
365
  let names = this.getDigest(logicalPath) ? [logicalPath] :
package/lib/object.js CHANGED
@@ -11,6 +11,7 @@ const { OcflStore } = require('./store.js');
11
11
  const { OcflExtension } = require('./extension.js');
12
12
  const { NotImplementedError } = require('./error.js');
13
13
  const { parallelize, dataSourceAsIterable, isDirEmpty, findNamasteVersion } = require('./utils');
14
+ const { format } = require('util');
14
15
  const { OCFL_VERSION, OCFL_VERSIONS, INVENTORY_NAME, NAMASTE_PREFIX_OBJECT, NAMASTE_T } = require('./constants').OcflConstants;
15
16
 
16
17
  class UPDATE_MODE extends Enum {
@@ -31,18 +32,86 @@ const DIGEST = OcflDigest.CONTENT;
31
32
  * @property {string} [ocflVersion=c.OCFL_VERSION] - Ocfl version. Only applies to a newly created object.
32
33
  * @property {OcflExtension[]} [extensions] - Reference to existing extensions defined outside of the object, such as in the storage root.
33
34
  */
34
- /**
35
- * @typedef {{logicalPath: string; digest: string; contentPath: string;}} FileRef
36
- */
37
35
  /**
38
36
  * @typedef {UPDATE_MODE | ('MERGE' | 'REPLACE') } ObjectUpdateMode
37
+ *
38
+ * @typedef {{logicalPath?: string, digest?: string, contentPath?: string, version?: string}} FileRef
39
+ * @typedef {{logicalPath: string, version?: string}} FileRefLogical
40
+ * @typedef {{digest: string}} FileRefDigest
41
+ * @typedef {{contentPath: string}} FileRefContent
39
42
  */
40
- /**
41
- *
42
- */
43
- const defaultConfig = {
44
- ocflVersion: OCFL_VERSION
45
- };
43
+
44
+ class OcflObjectFile {
45
+ #ocflObject;
46
+ /**
47
+ *
48
+ * @param {OcflObjectImpl} ocflObject
49
+ * @param {FileRef} fileRef
50
+ */
51
+ constructor(ocflObject, fileRef) {
52
+ this.#ocflObject = ocflObject;
53
+ // for (const key in ['logicalPath', 'version', 'digest', 'contentPath']) {
54
+ // if (fileRef[key]) this[key] = fileRef[key];
55
+ // }
56
+ if (fileRef.logicalPath) this.logicalPath = fileRef.logicalPath;
57
+ if (fileRef.contentPath) this.contentPath = fileRef.contentPath;
58
+ if (fileRef.digest) this.digest = fileRef.digest;
59
+ if (fileRef.version) this.version = fileRef.version;
60
+ }
61
+
62
+ /**
63
+ * Resolve contentPath given either the `logicalPath` and `version`, `digest`, or `contentPath`.
64
+ */
65
+ async #resolveContentPath() {
66
+ let contentPath = this.contentPath;
67
+ if (!contentPath) {
68
+ let inv = await this.#ocflObject.getInventory();
69
+ let version = !this.version || this.version === 'latest' ? inv.head : this.version;
70
+ let digest = this.digest || inv.getDigest(this.logicalPath, version);
71
+ contentPath = inv.getContentPath(digest);
72
+ if (!contentPath) throw new Error(`Cannot find content "${this.toString()}" in the OCFL Object "${this.#ocflObject.id}" version "${this.version || 'latest'}"`);
73
+ }
74
+ return contentPath;
75
+ }
76
+
77
+ toString() {
78
+ let o = {};
79
+ for (const key in ['logicalPath', 'version', 'digest', 'contentPath']) {
80
+ if (this[key]) o[key] = this[key];
81
+ }
82
+ return format('%O', o);
83
+ }
84
+
85
+ /**
86
+ * Get the file content as a buffer
87
+ * @param {*} [options] Options to be passed to underlying data store specific implementation
88
+
89
+ * @return {Promise<Buffer>}
90
+ */
91
+ async asBuffer(options) {
92
+ return this.#ocflObject.readFile(await this.#resolveContentPath(), options);
93
+ }
94
+
95
+ /**
96
+ * Get the file content as a string
97
+ * @param {BufferEncoding} [encoding='utf8'] String encoding, default to utf8
98
+ * @return {Promise<string>}
99
+ */
100
+ async asString(encoding = 'utf8') {
101
+ // @ts-ignore
102
+ return this.asBuffer(encoding);
103
+ }
104
+
105
+ /**
106
+ * Get the file content as a stream
107
+ * @param {*} [options] Options to be passed to underlying data store specific implementation
108
+ * @return {Promise<NodeJS.ReadableStream>}
109
+ */
110
+ async asStream(options) {
111
+ return this.#ocflObject.createReadStream(await this.#resolveContentPath(), options);
112
+ }
113
+ }
114
+
46
115
 
47
116
  /**
48
117
  * Interface class representing OCFL Object
@@ -66,8 +135,6 @@ class OcflObject {
66
135
 
67
136
  toJSON() { return { root: this.root, id: this.id }; }
68
137
 
69
- async inventory(id, dir) { }
70
-
71
138
  /**
72
139
  * If a cache exists, return the cached inventory. Otherwise read it from the storage and cache it.
73
140
  * @param {string} [version] - Full name of the version, eg: v1, v2, v3
@@ -124,6 +191,7 @@ class OcflObject {
124
191
  }
125
192
 
126
193
  async load() {
194
+ this._inventory = {};
127
195
  await this.getInventory();
128
196
  //await this.loadExtensions();
129
197
  return;
@@ -131,47 +199,32 @@ class OcflObject {
131
199
 
132
200
  /**
133
201
  * Iterate through the content files contained in the specified version.
134
- * Returns an array of logical paths in no particular order
202
+ * Returns an array of FileRef which consists of logical path, content path, and digest, in no particular order
135
203
  * @param {string} [version]
136
- * @return {Promise<Generator<FileRef, void, unknown>>}
204
+ * @return {Promise<Generator<OcflObjectFile, void, unknown>>}
137
205
  */
138
206
  async files(version) { throw new Error('Not Implemented'); }
139
207
 
140
208
  /**
141
- * Get content file as buffer. Either one of the logicalPath, contentPath, or digest parameters must be specified.
142
- * @param {Object} opt
143
- * @param {string} [opt.logicalPath] - Logical path of the file, eg: `test`
144
- * @param {string} [opt.contentPath] - Content path of the file, eg: `v1/content/test`
145
- * @param {string} [opt.digest] - Digest of the file content
146
- * @param {string} [opt.version] - Version name
147
- * @param {*} [opt.options] - Options to be passed to underlying fs
148
- * @return {Promise<Buffer>}
149
- */
150
- async getAsBuffer(opt) { throw new Error('Not Implemented'); }
151
-
209
+ * Get a file by its logical path and version. If version is omitted, it defaults to the last version.
210
+ * @callback GetFileByLogical
211
+ * @param {string} logicalPath Logical path of the file, eg: 'test'
212
+ * @param {string} [version='latest'] The object version name, eg: 'v1', 'v2'. Default to 'latest'
213
+ * @returns {OcflObjectFile}
214
+ */
152
215
  /**
153
- * Get content file as string
154
- * @param {Object} opt
155
- * @param {string} [opt.logicalPath]
156
- * @param {string} [opt.contentPath]
157
- * @param {string} [opt.digest]
158
- * @param {string} [opt.version]
159
- * @param {BufferEncoding} [opt.encoding]
160
- * @return {Promise<string>}
216
+ * Get a file by either the [logical path and version], digest, or content path.
217
+ * @callback GetFileByOpt
218
+ * @param { FileRefLogical | FileRefDigest | FileRefContent } opt A choice of logical path (eg: 'test') and version (eg: 'v1'),
219
+ * digest of the file content, or content path (eg: 'v1/content/test')
220
+ * @returns {OcflObjectFile}
161
221
  */
162
- async getAsString(opt) { throw new Error('Not Implemented'); }
163
-
164
222
  /**
165
- * Get content file as a stream. Either one of the logicalPath, contentPath, or digest parameters must be specified.
166
- * @param {Object} opt
167
- * @param {string} [opt.logicalPath] - Logical path of the file, eg: `test`
168
- * @param {string} [opt.contentPath] - Content path of the file, eg: `v1/content/test`
169
- * @param {string} [opt.digest] - Digest of the file content
170
- * @param {string} [opt.version] - Version name
171
- * @param {*} [opt.options] - Options to be passed to underlying fs
172
- * @return {Promise<NodeJS.ReadableStream>}
223
+ * Retrieve the content of a file either as string, buffer, or stream.
224
+ * @type { GetFileByLogical & GetFileByOpt }
173
225
  */
174
- async getAsStream(opt) { throw new Error('Not Implemented'); }
226
+ // @ts-ignore
227
+ getFile = function (opt, version) { }
175
228
 
176
229
  /**
177
230
  * Count the number of files contained in this object
@@ -187,7 +240,9 @@ class OcflObject {
187
240
  * The existing directory may or may not be a valid OCFL Object.
188
241
  * @return {Promise<boolean>}
189
242
  */
190
- async exists() { throw new Error('Not Implemented'); }
243
+ async exists() { throw new Error('Not Implemented'); }
244
+
245
+ //async inventory(version = 'latest') { }
191
246
 
192
247
  }
193
248
 
@@ -298,18 +353,8 @@ class OcflObjectImpl extends OcflObject {
298
353
  return t;
299
354
  }
300
355
 
301
- /**
302
- * Return absolute path to the `contentPath` given either the `logicalPath`, `digest` or `contentPath` with a specific version
303
- * @param {*} param0
304
- * @returns
305
- */
306
- async _resolveContentPath({ logicalPath = '', contentPath = '', digest = '', version = '' }) {
307
- if (!contentPath) {
308
- let inv = await this.getInventory();
309
- version = version || inv.head;
310
- contentPath = inv.getContentPath(digest || inv.getDigest(logicalPath, version));
311
- }
312
- return contentPath;
356
+ async createReadStream(filePath, options) {
357
+ return this.#store.createReadStream(path.join(this.root, filePath), options);
313
358
  }
314
359
 
315
360
  /**
@@ -321,54 +366,44 @@ class OcflObjectImpl extends OcflObject {
321
366
  }} ReadFileFn
322
367
  */
323
368
  /**
324
- * Read a file inside an object.
325
- * @param filePath - The file path relative to the object root.
369
+ * Read a file inside an object.
370
+ * @type {ReadFileFn} abc
371
+ * @param {string} relPath - The file path relative to the object root.
326
372
  * @param options - Options to be passed to the underlying method
327
- * @type ReadFileFn
328
373
  */
329
- readFile = async function (filePath, options) {
330
- return this.#store.readFile(path.join(this.root, filePath), options);
374
+ readFile = async function (relPath, options) {
375
+ return this.#store.readFile(path.join(this.root, relPath), options);
331
376
  }
332
377
 
333
- /**
334
- *
335
- * @param {Object} opt
336
- * @param {string} [opt.logicalPath]
337
- * @param {string} [opt.contentPath]
338
- * @param {string} [opt.digest]
339
- * @param {string} [opt.version]
340
- * @param {*} [opt.options]
341
- */
342
- async getAsBuffer(opt) {
343
- let p = await this._resolveContentPath(opt);
344
- if (!p) throw new Error(`Cannot find content "${opt.logicalPath || opt.contentPath || opt.digest}" in the OCFL Object "${this.#id}" version "${opt.version || 'latest'}"`);
345
- return this.readFile(p, opt.options);
378
+ getFile = function (opt, version) {
379
+ var obj = this;
380
+ var opt_ = typeof opt === 'string' ? { logicalPath: opt, version: version } : opt;
381
+ return new OcflObjectFile(this, opt_);
346
382
  }
347
383
 
348
- /**
349
- *
350
- * @param {Object} opt
351
- * @param {string} [opt.logicalPath]
352
- * @param {string} [opt.contentPath]
353
- * @param {string} [opt.digest]
354
- * @param {string} [opt.version]
355
- * @param {BufferEncoding} [opt.encoding]
356
- * @return {Promise<string>}
357
- */
358
- async getAsString(opt) {
359
- // @ts-ignore
360
- return this.getAsBuffer({ ...opt, options: opt.encoding || 'utf8' });
361
- }
362
-
363
- async createReadStream(filePath, options) {
364
- return this.#store.createReadStream(path.join(this.root, filePath), options);
365
- }
366
-
367
- async getAsStream(opt) {
368
- let p = await this._resolveContentPath(opt);
369
- if (!p) throw new Error(`Cannot find content "${opt.logicalPath || opt.contentPath || opt.digest}" in the OCFL Object "${this.#id}" version "${opt.version || 'latest'}"`);
370
- return this.#store.createReadStream(p, opt.options);
371
- }
384
+ // async _resolveContentPath({ logicalPath = '', contentPath = '', digest = '', version = '' }) {
385
+ // if (!contentPath) {
386
+ // let inv = await this.getInventory();
387
+ // version = version && version !== 'latest' ? version : inv.head;
388
+ // contentPath = inv.getContentPath(digest || inv.getDigest(logicalPath, version));
389
+ // }
390
+ // return contentPath;
391
+ // }
392
+
393
+ // async getAsBuffer(opt) {
394
+ // let p = await this._resolveContentPath(opt);
395
+ // if (!p) throw new Error(`Cannot find content "${opt.logicalPath || opt.contentPath || opt.digest}" in the OCFL Object "${this.#id}" version "${opt.version || 'latest'}"`);
396
+ // return this.readFile(p, opt.options);
397
+ // }
398
+ // async getAsString(opt) {
399
+ // // @ts-ignore
400
+ // return this.getAsBuffer({ ...opt, options: opt.encoding || 'utf8' });
401
+ // }
402
+ // async getAsStream(opt) {
403
+ // let p = await this._resolveContentPath(opt);
404
+ // if (!p) throw new Error(`Cannot find content "${opt.logicalPath || opt.contentPath || opt.digest}" in the OCFL Object "${this.#id}" version "${opt.version || 'latest'}"`);
405
+ // return this.createReadStream(p, opt.options);
406
+ // }
372
407
 
373
408
  /**
374
409
  * Copy all content of this Object to a directory in local filesystem
@@ -385,7 +420,13 @@ class OcflObjectImpl extends OcflObject {
385
420
  */
386
421
  async files(version) {
387
422
  let inv = await this.getInventory();
388
- return inv.files(version);
423
+ let o = this;
424
+ function* files() {
425
+ for (const f of inv.files(version)) {
426
+ yield new OcflObjectFile(o, f);
427
+ }
428
+ }
429
+ return files();
389
430
  }
390
431
 
391
432
 
package/lib/storage.js CHANGED
@@ -21,7 +21,8 @@ const DEFAULT_LAYOUT = HashedNTupleStorageLayout;
21
21
  */
22
22
 
23
23
  class OcflStorage {
24
- static LAYOUT;
24
+ //static LAYOUT;
25
+ ocflVersion;
25
26
 
26
27
  /**
27
28
  * Get the storage root
@@ -100,6 +101,7 @@ class OcflStorage {
100
101
  async delete(id) {
101
102
  return this.remove(id);
102
103
  }
104
+
103
105
  async purge(id) {
104
106
  return this.remove(id);
105
107
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ocfl/ocfl",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Oxford Common File Layout (OCFL) JS library",
5
5
  "main": "index.js",
6
6
  "exports": {