@ocfl/ocfl-fs 0.1.2 → 0.2.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/README.md +19 -1
- package/lib/store.js +60 -12
- package/lib/types.d.ts +5 -3
- package/lib/utils.js +62 -0
- package/package.json +12 -7
package/README.md
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
-
# ocfl-
|
|
1
|
+
# ocfl-fs - OCFL implementation using Filesystem storage backend for Node.js
|
|
2
2
|
This is JavaScript/Node.js library to create and interact with Oxford Common File Layout (OCFL) storage and objects within it.
|
|
3
3
|
This package implements the OCFL client that stores storage root and its objects on a locally attached filesystem.
|
|
4
4
|
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
npm install @ocfl/ocfl-fs
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
The storage and object methods accept an optional fs parameter, which can be any module that implements Node.js fs module API. By default, it will use the built in fs module.
|
|
12
|
+
|
|
13
|
+
const ocfl = require('@ocfl/ocfl-fs');
|
|
14
|
+
const storage = ocfl.storage({root: '/var/data/myocfl'});
|
|
15
|
+
|
|
16
|
+
For example, by passing it memfs (https://www.npmjs.com/package/memfs) the OCFL storage will be created in memory using the memfs module:
|
|
17
|
+
|
|
18
|
+
const ocfl = require('@ocfl/ocfl-fs');
|
|
19
|
+
const fs = require('memfs');
|
|
20
|
+
const storage = ocfl.storage({root: '/var/data/myocfl'}, {fs});
|
|
21
|
+
|
|
22
|
+
For common usage documentation please refer to the [README.md in GitHub](https://github.com/Arkisto-Platform/ocfl-js)
|
package/lib/store.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
//@ts-check
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const {
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const { Readable, Writable } = require('node:stream');
|
|
5
5
|
|
|
6
6
|
const { OcflStore, OcflConstants } = require('@ocfl/ocfl');
|
|
7
|
+
const { opendir } = require('./utils.js');
|
|
7
8
|
const { INVENTORY_NAME } = OcflConstants;
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -18,11 +19,6 @@ class OcflFsStore extends OcflStore {
|
|
|
18
19
|
/** @type {Map<Object, OcflFsStore>} */
|
|
19
20
|
static instances = new Map();
|
|
20
21
|
|
|
21
|
-
/**
|
|
22
|
-
*
|
|
23
|
-
* @param {Object} options
|
|
24
|
-
* @param {typeof fs} [options.fs]
|
|
25
|
-
*/
|
|
26
22
|
// static getInstance({fs = require('fs')} = {}) {
|
|
27
23
|
// let store = OcflFsStore.instances.get(fs);
|
|
28
24
|
// if (!store) {
|
|
@@ -42,8 +38,8 @@ class OcflFsStore extends OcflStore {
|
|
|
42
38
|
this.fs = config?.fs ?? fs;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
|
-
async stat(filePath) {
|
|
46
|
-
return this.fs.promises.stat(filePath);
|
|
41
|
+
async stat(filePath) {
|
|
42
|
+
return this.fs.promises.stat(filePath);
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
async createReadStream(filePath, options) {
|
|
@@ -51,11 +47,20 @@ class OcflFsStore extends OcflStore {
|
|
|
51
47
|
return this.fs.createReadStream(filePath, options);
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
async createWriteStream(filePath, options) {
|
|
50
|
+
async createWriteStream(filePath, options) {
|
|
55
51
|
await this.fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
56
52
|
return this.fs.createWriteStream(filePath, options);
|
|
57
53
|
}
|
|
58
54
|
|
|
55
|
+
async createReadable(filePath, options) {
|
|
56
|
+
return Readable.toWeb(this.fs.createReadStream(filePath, options));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async createWritable(filePath, options) {
|
|
60
|
+
await this.fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
61
|
+
return Writable.toWeb(this.fs.createWriteStream(filePath, options));
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
async readFile(filePath, options) {
|
|
60
65
|
return this.fs.promises.readFile(filePath, options);
|
|
61
66
|
//if (err.code !== 'ENOENT') throw validation.createError(34, invPath);
|
|
@@ -72,11 +77,54 @@ class OcflFsStore extends OcflStore {
|
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
async opendir(filePath, options) {
|
|
75
|
-
return this.fs.promises.opendir(filePath, options);
|
|
80
|
+
return this.fs.promises.opendir(filePath, options);
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
async readdir(filePath, options) {
|
|
79
|
-
return this.fs.promises.readdir(filePath, options);
|
|
84
|
+
return this.fs.promises.readdir(filePath, options);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {string} dirPath
|
|
89
|
+
* @param {OpenDirOptions} [options]
|
|
90
|
+
* @return {Promise<AsyncIterableIterator<{name: string, path: string, size: number, lastModified: Date}>> }
|
|
91
|
+
*/
|
|
92
|
+
async list(dirPath, { encoding = 'utf8', bufferSize = 32, recursive = false } = {}) {
|
|
93
|
+
const {opendir, stat} = this.fs.promises;
|
|
94
|
+
const maxBuffer = bufferSize || 1;
|
|
95
|
+
async function* generator() {
|
|
96
|
+
/** @type Promise<{ name: string, path: string, size: number, lastModified: Date}>[] */
|
|
97
|
+
let buffer = [];
|
|
98
|
+
let dirp;
|
|
99
|
+
const dirQueue = [opendir(dirPath, { encoding, bufferSize })];
|
|
100
|
+
while (dirp = dirQueue.shift()) {
|
|
101
|
+
const dir = await dirp;
|
|
102
|
+
for await (const de of dir) {
|
|
103
|
+
const cdp = path.join(dir.path, de.name);
|
|
104
|
+
if (recursive && de.isDirectory()) {
|
|
105
|
+
dirQueue.push(opendir(cdp, { encoding, bufferSize }));
|
|
106
|
+
} else {
|
|
107
|
+
let f = stat(cdp).then(stats => ({
|
|
108
|
+
name: de.name,
|
|
109
|
+
path: path.relative(dirPath, cdp),
|
|
110
|
+
size: stats.size,
|
|
111
|
+
lastModified: stats.mtime
|
|
112
|
+
}));
|
|
113
|
+
if (buffer.length < maxBuffer) {
|
|
114
|
+
buffer.push(f);
|
|
115
|
+
} else {
|
|
116
|
+
yield buffer.shift();
|
|
117
|
+
buffer.push(f);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
let file;
|
|
123
|
+
while (file = buffer.shift()) {
|
|
124
|
+
yield file;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return generator();
|
|
80
128
|
}
|
|
81
129
|
|
|
82
130
|
async mkdir(filePath, options = { recursive: true }) {
|
package/lib/types.d.ts
CHANGED
package/lib/utils.js
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const fs = require('node:fs/promises');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wrapper for fs.opendir that reimplements the recursion and
|
|
6
|
+
* returns just the file without the directory itself
|
|
7
|
+
* fs.opendir has a bug that gives wrong result when recursive option is used
|
|
8
|
+
* @param {string} dirPath
|
|
9
|
+
* @param {OpenDirOptions} [options]
|
|
10
|
+
*/
|
|
11
|
+
async function* opendir(dirPath, { encoding = 'utf8', bufferSize = 32, recursive = false } = {}) {
|
|
12
|
+
/** @type Promise<{ name: string, path: string, size: number, lastModified: Date}>[] */
|
|
13
|
+
let buffer = [];
|
|
14
|
+
let dirp;
|
|
15
|
+
const maxBuffer = bufferSize || 1;
|
|
16
|
+
const dirQueue = [fs.opendir(dirPath, { encoding, bufferSize })];
|
|
17
|
+
while (dirp = dirQueue.shift()) {
|
|
18
|
+
const dir = await dirp;
|
|
19
|
+
for await (const de of dir) {
|
|
20
|
+
const cdp = path.join(dir.path, de.name);
|
|
21
|
+
if (recursive && de.isDirectory()) {
|
|
22
|
+
dirQueue.push(fs.opendir(cdp));
|
|
23
|
+
} else {
|
|
24
|
+
let f = fs.stat(cdp).then(stats => ({
|
|
25
|
+
name: de.name,
|
|
26
|
+
path: path.relative(dirPath, cdp),
|
|
27
|
+
size: stats.size,
|
|
28
|
+
lastModified: stats.mtime
|
|
29
|
+
}));
|
|
30
|
+
if (buffer.length < maxBuffer) {
|
|
31
|
+
buffer.push(f);
|
|
32
|
+
} else {
|
|
33
|
+
yield buffer.shift();
|
|
34
|
+
buffer.push(f);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
let file;
|
|
40
|
+
while (file = buffer.shift()) {
|
|
41
|
+
yield file;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// (async function() {
|
|
46
|
+
// var a = opendir('/home/alvinsw/.ssh', {recursive: true})
|
|
47
|
+
// var b = 'a';
|
|
48
|
+
// console.log(await a);
|
|
49
|
+
|
|
50
|
+
// let c = 0;
|
|
51
|
+
// for await (const f of await opendir('/home/alvinsw/uqlang', {recursive: true})) {
|
|
52
|
+
// c++;
|
|
53
|
+
// }
|
|
54
|
+
// console.log(c);
|
|
55
|
+
// c=0;
|
|
56
|
+
// for await (const f of await fs.opendir('/home/alvinsw/uqlang', {recursive: true})) {
|
|
57
|
+
// c++;
|
|
58
|
+
// }
|
|
59
|
+
// console.log(c);
|
|
60
|
+
// })();
|
|
61
|
+
|
|
62
|
+
module.exports = { opendir }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ocfl/ocfl-fs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Oxford Common File Layout (OCFL) JS client library for filesystem storage",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
@@ -37,14 +37,19 @@
|
|
|
37
37
|
"license": "GPL-3.0-or-later",
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/fs-extra": "^9.0.13",
|
|
40
|
-
"
|
|
41
|
-
"jsdoc": "^3.
|
|
42
|
-
"
|
|
40
|
+
"@types/node": "^18",
|
|
41
|
+
"clean-jsdoc-theme": "^4.3.0",
|
|
42
|
+
"jsdoc": "^4.0.3",
|
|
43
|
+
"mocha": "^10.7.3",
|
|
43
44
|
"typedoc": "^0.22.15",
|
|
44
|
-
"@ocfl/ocfl-tests": "
|
|
45
|
+
"@ocfl/ocfl-tests": "*"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
|
-
"@ocfl/ocfl": "0.
|
|
48
|
+
"@ocfl/ocfl": "0.2.2"
|
|
48
49
|
},
|
|
49
|
-
"files": [
|
|
50
|
+
"files": [
|
|
51
|
+
"index.js",
|
|
52
|
+
"index.mjs",
|
|
53
|
+
"lib"
|
|
54
|
+
]
|
|
50
55
|
}
|