@ocfl/ocfl 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/enum.js CHANGED
@@ -1,92 +1,90 @@
1
1
  /**
2
2
  * Enumeration tool for working with a set of options
3
- * @module enum
3
+ * @module enumeration
4
4
  */
5
5
 
6
+ // const COLOR = (c => {
7
+ // let r = /** @type {{[key in (typeof c)[number]]: string}} */({});
8
+ // return r;
9
+ // })(/** @type {const} */(['red', 'green', 'blue']));
10
+ // COLOR.blue
11
+ const constantsSymbol = Symbol('constants');
12
+
6
13
  /**
7
- * Create enumeration type by extending the Enum class and
8
- * defining the name and value of each constant as static fields.
9
- * @template V
14
+ * Create enumeration type by passing the list of constants as arguments of string
15
+ * @template {readonly string[]} const T
16
+ * @param {T} constants
10
17
  */
11
- class Enum {
12
- /** @type {string} */
13
- #name;
14
- /** @type {V} */
15
- #value;
16
18
 
17
- /**
18
- * Return an existing constant that represents the specified value
19
- * @type {<T extends typeof Enum>(this: T, value: any) => InstanceType<T>}
20
- */
21
- static of = function (value, c=this) {
22
- //if (value instanceof c) return value;
23
- for (let name in c) {
24
- if (c[name].value === value) return c[name];
19
+ function enumeration(constants) {
20
+ class Enum {
21
+ /** @type {string} */
22
+ #name;
23
+ /** @type {number} */
24
+ #ordinal;
25
+ constructor(name, ordinal) {
26
+ this.#name = name;
27
+ this.#ordinal = ordinal;
28
+ //Object.defineProperty(this, 'name', { writable: false, enumerable: true, value: name });
29
+ //Object.defineProperty(this, 'ordinal', { writable: false, enumerable: true, value: ordinal });
25
30
  }
26
- }
27
-
28
- /**
29
- * Check if the Enum type contains an Enum instance
30
- * @param {Enum} instance
31
- */
32
- static has(instance) {
33
- for (let name in this) {
34
- if (this[name] === instance) return true;
31
+ get name() { return this.#name }
32
+ get ordinal() { return this.#ordinal }
33
+ toString() {
34
+ return this.name;
35
35
  }
36
- return false;
37
- }
38
-
39
- static ofOrdinal(index, c=this) {
40
- return c['_enums'][index];
41
- }
42
-
43
- static freeze(c=this) {
44
- return Object.freeze(c);
45
- }
46
-
47
- static size(c=this) {
48
- return c['_enums'].length;
49
- }
50
-
51
- static [Symbol.iterator]() {
52
- return this['_enums'][Symbol.iterator]();
53
- }
54
-
55
- /**
56
- * @param {V} [value]
57
- */
58
- constructor(value) {
59
- let c = this.constructor;
60
- if (!Object.hasOwn(c, '_enums')) {
61
- let p = Object.getPrototypeOf(c);
62
- Object.defineProperty(c, '_enums', { value: p['_enums']?.slice(0) ?? [] });
36
+ /** @returns {Iterator<string>} */
37
+ static [Symbol.iterator]() {
38
+ return this[constantsSymbol][Symbol.iterator]();
63
39
  }
64
- this.#value = value;
65
- this.ordinal = c['_enums'].length;
66
- c['_enums'].push(this);
67
- Object.freeze(this);
68
40
  }
69
-
70
- get name() {
71
- if (!this.#name) {
72
- let ec = this.constructor;
73
- for (let key in ec) {
74
- if (ec[key] === this) {
75
- this.#name = key;
76
- break;
77
- }
78
- }
79
- }
80
- return this.#name;
41
+ /** @typedef { typeof Enum & {[key in T[number]]: Enum} } EnumType */
42
+ let count = 0;
43
+ for (let c of constants) {
44
+ Object.defineProperty(Enum, c, {
45
+ value: Object.freeze(new Enum(c, count++)),
46
+ writable: false,
47
+ enumerable: true
48
+ });
81
49
  }
50
+ Object.defineProperty(Enum, constantsSymbol, {
51
+ value: constants,
52
+ writable: false,
53
+ enumerable: false
54
+ });
55
+ return /** @type {EnumType} */(Object.freeze(Enum));
56
+ }
82
57
 
83
- get value() {
84
- return this.#value ?? this.name;
58
+ /**
59
+ * Return an existing constant that represents the specified value
60
+ * @type {{<T extends abstract new() => any>(type: T, value: any): InstanceType<T>}}
61
+ */
62
+ enumeration.of = function (type, value) {
63
+ if (value instanceof type && type[value.name]) return value;
64
+ else if (typeof value === 'string') {
65
+ return type[/**@type {string}*/(value)] || (() => {
66
+ for (let name in type) {
67
+ if (name.toUpperCase() === value.toUpperCase()) return type[name];
68
+ }
69
+ })();
85
70
  }
71
+ }
86
72
 
87
- toString() {
88
- return this.value?.toString();
89
- }
73
+ /**
74
+ * Return an existing constant based on the index
75
+ * @template T
76
+ * @param {new() => T} type
77
+ * @param {number} index
78
+ * @return {T}
79
+ */
80
+ enumeration.at = function (type, index) {
81
+ return type[type[constantsSymbol][index]];
82
+ }
90
83
 
84
+ enumeration.size = function (type) {
85
+ return type[constantsSymbol].length;
91
86
  }
92
- module.exports = { Enum };
87
+
88
+ exports.enumeration = enumeration;
89
+ exports.default = { ...enumeration };
90
+ //export default { ...enumeration };
package/lib/extension.js CHANGED
@@ -1,6 +1,3 @@
1
- //const {} = require('./object');
2
- //const digest = require('./digest');
3
-
4
1
  const { NotImplementedError } = require("./error");
5
2
 
6
3
  /** Index by Registered extension name */
@@ -10,8 +7,6 @@ const extensionByClassName = {};
10
7
 
11
8
  const storageLayout = {};
12
9
 
13
- /** @typedef {{ extensionName?: string;[key: string]: any; }} OcflExtensionConfig */
14
-
15
10
  // class OcflExtensionConfig {
16
11
  // /** User defined config */
17
12
  // definedConfig;
@@ -20,6 +15,7 @@ const storageLayout = {};
20
15
  // }
21
16
 
22
17
  // }
18
+ /** @typedef {import('../index.js').StorageLayout} StorageLayout */
23
19
 
24
20
  /**
25
21
  * @template {{}} C
@@ -76,16 +72,6 @@ class OcflExtension {
76
72
  extensionClass.setup(require('./index.js'));
77
73
  }
78
74
 
79
- /**
80
- * @template {typeof OcflExtension} T
81
- * @param {OcflExtensionConfig} [config]
82
- * @this {T}
83
- * @return {InstanceType<T>}
84
- */
85
- static create2(config){
86
- if (!config?.extensionName || this.NAME === config.extensionName) return /**@type {InstanceType<T>}*/(new this(config));
87
- return OcflExtension.class(config.extensionName).create2(config);
88
- }
89
75
  /**
90
76
  * Create an instance of the extension
91
77
  * @type {<T extends typeof OcflExtension>(this: T, config?: OcflExtensionConfig) => InstanceType<T>}
@@ -99,9 +85,9 @@ class OcflExtension {
99
85
 
100
86
  static get forObject() { return false; }
101
87
  static get forStorage() { return false; }
102
- static async setup(ocfl) { };
103
88
  static get NAME() { return '0000-example-extension' }
104
89
  static get DESCRIPTION() { return 'Example extension' }
90
+ static async setup(ocfl) { };
105
91
 
106
92
  /**
107
93
  *
@@ -192,6 +178,8 @@ class OcflStorageLayout extends OcflExtension {
192
178
  */
193
179
  constructor(config, defaultConfig) { super(config, defaultConfig); }
194
180
 
181
+ async validate() {}
182
+
195
183
  /**
196
184
  * Map an object identifier to a path
197
185
  * @param {string} id - The identifier of the OCFL Object
@@ -1,5 +1,6 @@
1
1
  const { OcflStorageLayout } = require("../extension");
2
2
  const { OcflDigest } = require("../digest");
3
+ const { enumeration } = require("../enum");
3
4
  const path = require("path");
4
5
 
5
6
  function encodeIdentifier(id) {
@@ -31,10 +32,11 @@ class HashAndIdNTupleStorageLayout extends OcflStorageLayout {
31
32
  constructor(config) {
32
33
  super(config, DefaultConfig);
33
34
  let c = this.parameters;
34
- if (!OcflDigest.FIXITY.of(c.digestAlgorithm)) throw new Error('Invalid digestAlgorithm');
35
- let digest = OcflDigest.digestSync(c.digestAlgorithm, 'test');
35
+ const algo = enumeration.of(OcflDigest.FIXITY, c.digestAlgorithm)?.name;
36
+ if (!algo) throw new Error('Invalid digestAlgorithm');
37
+ let digestLength = OcflDigest.getHexDigestLength(algo);
36
38
  let p = c.numberOfTuples * c.tupleSize
37
- if (p > digest.length) throw new Error('Product of numberOfTuples and tupleSize is greater than the number of characters in the hex encoded digest.');
39
+ if (p > digestLength) throw new Error('Product of numberOfTuples and tupleSize is greater than the number of characters in the hex encoded digest.');
38
40
  }
39
41
 
40
42
  /**
@@ -1,6 +1,7 @@
1
1
  const { OcflStorageLayout } = require("../extension");
2
2
  const { OcflDigest } = require("../digest");
3
3
  const path = require("path");
4
+ const { enumeration } = require("../enum");
4
5
 
5
6
  const DefaultConfig = {
6
7
  extensionName: '0004-hashed-n-tuple-storage-layout',
@@ -28,11 +29,12 @@ class HashedNTupleStorageLayout extends OcflStorageLayout {
28
29
  constructor(config) {
29
30
  super(config, DefaultConfig);
30
31
  let c = this.parameters;
31
- if (!OcflDigest.FIXITY.of(c.digestAlgorithm)) throw new Error('Invalid digestAlgorithm');
32
- let digest = OcflDigest.digestSync(c.digestAlgorithm, 'test');
32
+ const algo = enumeration.of(OcflDigest.FIXITY, c.digestAlgorithm)?.name;
33
+ if (!algo) throw new Error('Invalid digestAlgorithm');
34
+ let digestLength = OcflDigest.getHexDigestLength(algo);
33
35
  let p = c.numberOfTuples * c.tupleSize
34
- if (p > digest.length) throw new Error('Product of numberOfTuples and tupleSize is greater than the number of characters in the hex encoded digest.');
35
- else if (p === digest.length && c.shortObjectRoot) throw new Error('shortObjectRoot cannot be set to true');
36
+ if (p > digestLength) throw new Error('Product of numberOfTuples and tupleSize is greater than the number of characters in the hex encoded digest.');
37
+ else if (p === digestLength && c.shortObjectRoot) throw new Error('shortObjectRoot cannot be set to true');
36
38
  }
37
39
 
38
40
  /**
@@ -0,0 +1,18 @@
1
+ //@ts-check
2
+
3
+ const {OcflExtension} = require("../extension");
4
+
5
+ /**
6
+ * Notes: this implementation does nothing because the 0009 extension is implemented directly in the OcflDigest class.
7
+ */
8
+ class DigestAlgorithm extends OcflExtension {
9
+ static get NAME() { return '0009-digest-algorithms' }
10
+
11
+ /**
12
+ *
13
+ * @param {import('ocfl')} ocfl
14
+ */
15
+ static async setup(ocfl) {}
16
+ }
17
+
18
+ module.exports = { DigestAlgorithm };
@@ -1,6 +1,6 @@
1
1
 
2
2
  module.exports = {
3
- ...require('./0001-digest-algorithms'),
3
+ ...require('./0009-digest-algorithms'),
4
4
  ...require('./0002-flat-direct-storage-layout'),
5
5
  ...require('./0003-hash-and-id-n-tuple-storage-layout'),
6
6
  ...require('./0004-hashed-n-tuple-storage-layout'),
package/lib/file.js ADDED
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @typedef {Object} FileRef
3
+ * @property {string} [contentPath] - Actual path to the file location relative to the ocfl object root.
4
+ * @property {string} [logicalPath] - A path that represents a file’s location in the logical state of an object.
5
+ * @property {string} [digest] - An algorithmic characterization of the contents of a file conforming to a standard digest algorithm.
6
+ * @property {string} [version] - Version of the OCFL object.
7
+ * @property {string} [lastModified] - Last modified date of the file.
8
+ * @property {string} [size] - Size of the file in bytes.
9
+ * @property {Object.<string, string>} [fixity] - Fixity information of the file.
10
+ */
11
+
12
+ /**
13
+ * @typedef {Object} FileStat
14
+ * @property {number} [size] - Size of the file in bytes.
15
+ * @property {Date} [atime] - Last access time of the file.
16
+ * @property {Date} [mtime] - Last modified time of the file.
17
+ * @property {Date} [ctime] - Creation time of the file.
18
+ */
19
+
20
+ /**
21
+ * Represent a file in an OCFL object
22
+ */
23
+ class OcflObjectFile {
24
+ #ocflObject;
25
+ #lastModified;
26
+ #size;
27
+
28
+ /**
29
+ *
30
+ * @param {import('./object').OcflObject} ocflObject
31
+ * @param {FileRef} fileRef
32
+ */
33
+ constructor(ocflObject, fileRef) {
34
+ this.#ocflObject = ocflObject;
35
+ // for (const key in ['logicalPath', 'version', 'digest', 'contentPath']) {
36
+ // if (fileRef[key]) this[key] = fileRef[key];
37
+ // }
38
+ if (fileRef.logicalPath) this.logicalPath = fileRef.logicalPath;
39
+ if (fileRef.contentPath) this.contentPath = fileRef.contentPath;
40
+ if (fileRef.digest) this.digest = fileRef.digest;
41
+ if (fileRef.version) this.version = fileRef.version;
42
+ if (fileRef.lastModified != null) this.#lastModified = fileRef.lastModified;
43
+ if (fileRef.size != null) this.#size = fileRef.size;
44
+ if (fileRef.fixity != null) this.fixity = fileRef.fixity;
45
+ }
46
+
47
+ /**
48
+ * Resolve contentPath given either the `logicalPath` and `version`, `digest`, or `contentPath`.
49
+ */
50
+ async #resolveContentPath() {
51
+ let contentPath = this.contentPath;
52
+ if (!contentPath) {
53
+ let inv = await this.#ocflObject.getInventory();
54
+ if (!inv) throw new Error(`OCFL Object "${this.#ocflObject.id}" does not exist`);
55
+ let version = !this.version || this.version === 'latest' ? inv.head : this.version;
56
+ let digest = this.digest || inv.getDigest(this.logicalPath, version);
57
+ contentPath = inv.getContentPath(digest);
58
+ if (!contentPath) throw new Error(`Cannot find content "${this.toString()}" in the OCFL Object "${this.#ocflObject.id}" version "${this.version || 'latest'}"`);
59
+ }
60
+ return contentPath;
61
+ }
62
+
63
+ /**
64
+ * Returns the last modified time of the file, in millisecond since the UNIX epoch (January 1st, 1970 at Midnight).
65
+ */
66
+ get lastModified() { return this.#lastModified }
67
+
68
+ /**
69
+ * Returns the file size in bytes.
70
+ */
71
+ get size() { return this.#size }
72
+
73
+ toString() {
74
+ let props = [];
75
+ for (const key in ['logicalPath', 'version', 'digest', 'contentPath']) {
76
+ if (this[key]) props.push(key + ": '" + this[key] + "'");
77
+ }
78
+ return `{ ${props.join(', ')} }`;
79
+ }
80
+
81
+ /**
82
+ * Get the file content as a buffer
83
+ * @param {*} [options] Options to be passed to underlying data store specific implementation
84
+
85
+ * @return {Promise<Buffer>}
86
+ */
87
+ async buffer(options) {
88
+ return this.#ocflObject.readFile(await this.#resolveContentPath(), options);
89
+ }
90
+
91
+ /**
92
+ * @deprecated Use {@link buffer()}
93
+ */
94
+ async asBuffer(options) {
95
+ return this.buffer(options);
96
+ }
97
+
98
+ /**
99
+ * Get the file content as a string
100
+ * @param {BufferEncoding} [encoding='utf8'] String encoding, default to utf8
101
+ * @return {Promise<string>}
102
+ */
103
+ async text(encoding = 'utf8') {
104
+ // @ts-ignore
105
+ return this.buffer(encoding);
106
+ }
107
+ /**
108
+ * @deprecated Use {@link text()}
109
+ */
110
+ async asString(encoding = 'utf8') {
111
+ // @ts-ignore
112
+ return this.text(encoding);
113
+ }
114
+
115
+ /**
116
+ * Get the file content as a NodeJS stream
117
+ * @param {*} [options] Options to be passed to underlying data store specific implementation
118
+ * @return {Promise<ReadableStream>}
119
+ * @deprecated Use {@link stream()}
120
+ */
121
+ async asStream(options) {
122
+ return this.#ocflObject.createReadable(await this.#resolveContentPath(), options);
123
+ }
124
+
125
+ /**
126
+ * Get the file content as a ES Streams standard ReadableStream
127
+ * @param {*} [options] Options to be passed to underlying data store specific implementation
128
+ * @return {Promise<ReadableStream>}
129
+ */
130
+ async stream(options) {
131
+ return this.#ocflObject.createReadable(await this.#resolveContentPath(), options);
132
+ }
133
+
134
+ /**
135
+ * Get the file stat info
136
+ * @param {*} [options] Options to be passed to underlying data store specific implementation
137
+ * @return {Promise<FileStat>}
138
+ */
139
+ async stat(options) {
140
+ return this.#ocflObject.stat(await this.#resolveContentPath());
141
+ }
142
+
143
+ }
144
+
145
+ module.exports = {
146
+ OcflObjectFile
147
+ };
package/lib/index.js CHANGED
@@ -1,16 +1,4 @@
1
1
  // this is an internal index.js to avoid circular dependencies problem
2
- // const { OcflStorage, OcflStorageImpl } = require('./storage.js');
3
- // const { OcflObject,
4
- // OcflObjectImpl,
5
- // OcflObjectInventory,
6
- // OcflObjectTransactionImpl,
7
- // createObject,
8
- // createObjectProxy,
9
- // createTransactionProxy } = require('./object.js');
10
- // const { OcflConstants } = require('./constants.js');
11
- // const { OcflDigest } = require('./digest.js');
12
- // const { OcflExtension } = require('./extension.js');
13
-
14
2
  module.exports = {
15
3
  ...require('./constants.js'),
16
4
  ...require('./digest.js'),