@ocfl/ocfl-fs 0.2.0 → 0.3.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/index.js CHANGED
@@ -1,10 +1,14 @@
1
1
  //@ts-check
2
2
  const { OcflFsStore } = require('./lib/store.js');
3
- const { Ocfl } = require('@ocfl/ocfl');
3
+ const { implementOcfl } = require('@ocfl/ocfl');
4
4
  //const { default: ocfl } = require('./index.mjs');
5
5
 
6
6
  /** @type {import('./lib/store.js').OcflFsStoreConfig} */
7
7
  let defaultOptions;
8
8
 
9
- module.exports = new Ocfl(OcflFsStore, defaultOptions);
9
+ const ocfl = implementOcfl(OcflFsStore, defaultOptions);
10
+ const { OCFL_VERSIONS, StorageLayout, createStorage, loadStorage, object, storage, storageLayout } = ocfl;
11
+ module.exports = {
12
+ OCFL_VERSIONS, StorageLayout, createStorage, loadStorage, object, storage, storageLayout
13
+ };
10
14
  //module.exports.default = module.exports;
package/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './index.js';
2
- import ocfl from './index.js';
2
+ import * as ocfl from './index.js';
3
3
  export default ocfl;
4
+
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 { mkdir, copyFile, rename, unlink } = fs.promises;
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
  /**
@@ -37,8 +38,8 @@ class OcflFsStore extends OcflStore {
37
38
  this.fs = config?.fs ?? fs;
38
39
  }
39
40
 
40
- async stat(filePath) {
41
- return this.fs.promises.stat(filePath);
41
+ async stat(filePath) {
42
+ return this.fs.promises.stat(filePath);
42
43
  }
43
44
 
44
45
  async createReadStream(filePath, options) {
@@ -46,11 +47,20 @@ class OcflFsStore extends OcflStore {
46
47
  return this.fs.createReadStream(filePath, options);
47
48
  }
48
49
 
49
- async createWriteStream(filePath, options) {
50
+ async createWriteStream(filePath, options) {
50
51
  await this.fs.promises.mkdir(path.dirname(filePath), { recursive: true });
51
52
  return this.fs.createWriteStream(filePath, options);
52
53
  }
53
54
 
55
+ async createReadable(filePath, options) {
56
+ return /** @type {ReadableStream} */ (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
+
54
64
  async readFile(filePath, options) {
55
65
  return this.fs.promises.readFile(filePath, options);
56
66
  //if (err.code !== 'ENOENT') throw validation.createError(34, invPath);
@@ -58,6 +68,7 @@ class OcflFsStore extends OcflStore {
58
68
 
59
69
  async writeFile(filePath, data, options) {
60
70
  await this.fs.promises.mkdir(path.dirname(filePath), { recursive: true });
71
+ if (data instanceof ReadableStream) data = fs.ReadStream.fromWeb(data);
61
72
  await this.fs.promises.writeFile(filePath, data, options);
62
73
  }
63
74
 
@@ -67,11 +78,54 @@ class OcflFsStore extends OcflStore {
67
78
  }
68
79
 
69
80
  async opendir(filePath, options) {
70
- return this.fs.promises.opendir(filePath, options);
81
+ return this.fs.promises.opendir(filePath, options);
71
82
  }
72
83
 
73
84
  async readdir(filePath, options) {
74
- return this.fs.promises.readdir(filePath, options);
85
+ return this.fs.promises.readdir(filePath, options);
86
+ }
87
+
88
+ /**
89
+ * @param {string} dirPath
90
+ * @param {OpenDirOptions} [options]
91
+ * @return {Promise<AsyncIterableIterator<{name: string, path: string, size: number, lastModified: Date}>> }
92
+ */
93
+ async list(dirPath, { encoding = 'utf8', bufferSize = 32, recursive = false } = {}) {
94
+ const {opendir, stat} = this.fs.promises;
95
+ const maxBuffer = bufferSize || 1;
96
+ async function* generator() {
97
+ /** @type Promise<{ name: string, path: string, size: number, lastModified: Date}>[] */
98
+ let buffer = [];
99
+ let dirp;
100
+ const dirQueue = [opendir(dirPath, { encoding, bufferSize })];
101
+ while (dirp = dirQueue.shift()) {
102
+ const dir = await dirp;
103
+ for await (const de of dir) {
104
+ const cdp = path.join(dir.path, de.name);
105
+ if (recursive && de.isDirectory()) {
106
+ dirQueue.push(opendir(cdp, { encoding, bufferSize }));
107
+ } else {
108
+ let f = stat(cdp).then(stats => ({
109
+ name: de.name,
110
+ path: path.relative(dirPath, cdp),
111
+ size: stats.size,
112
+ lastModified: stats.mtime
113
+ }));
114
+ if (buffer.length < maxBuffer) {
115
+ buffer.push(f);
116
+ } else {
117
+ yield buffer.shift();
118
+ buffer.push(f);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ let file;
124
+ while (file = buffer.shift()) {
125
+ yield file;
126
+ }
127
+ }
128
+ return generator();
75
129
  }
76
130
 
77
131
  async mkdir(filePath, options = { recursive: true }) {
@@ -80,6 +134,7 @@ class OcflFsStore extends OcflStore {
80
134
 
81
135
  async move(source, target) {
82
136
  try {
137
+ await this.fs.promises.mkdir(path.dirname(target), { recursive: true });
83
138
  return this.fs.promises.rename(source, target);
84
139
  } catch (error) {
85
140
  if (error.code === 'EXDEV') {
package/lib/types.d.ts CHANGED
@@ -1,3 +1,5 @@
1
- type TestType = {
2
- name: string;
3
- };
1
+ interface OpenDirOptions {
2
+ encoding?: BufferEncoding | undefined;
3
+ bufferSize?: number | undefined;
4
+ recursive?: boolean | undefined;
5
+ }
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.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Oxford Common File Layout (OCFL) JS client library for filesystem storage",
5
5
  "main": "index.js",
6
6
  "exports": {
@@ -15,8 +15,7 @@
15
15
  },
16
16
  "scripts": {
17
17
  "test": "mocha",
18
- "build-docs2": "typedoc --tsconfig tsconfig.json",
19
- "build-docs": "jsdoc -c jsdoc.json"
18
+ "build-docs": "typedoc --plugin typedoc-plugin-missing-exports"
20
19
  },
21
20
  "keywords": [
22
21
  "ocfl",
@@ -36,15 +35,17 @@
36
35
  ],
37
36
  "license": "GPL-3.0-or-later",
38
37
  "devDependencies": {
39
- "@types/fs-extra": "^9.0.13",
40
- "clean-jsdoc-theme": "^3.3.4",
41
- "jsdoc": "^3.6.10",
42
- "mocha": "^10.0.0",
43
- "typedoc": "^0.22.15",
44
- "@ocfl/ocfl-tests": "*"
38
+ "@ocfl/ocfl-tests": "*",
39
+ "@types/fs-extra": "^11.0.4",
40
+ "@types/node": "^24",
41
+ "clean-jsdoc-theme": "^4.3.0",
42
+ "jsdoc": "^4.0.3",
43
+ "mocha": "^11.7.5",
44
+ "typedoc": "^0.28.14",
45
+ "typedoc-plugin-missing-exports": "^4.1.2"
45
46
  },
46
47
  "dependencies": {
47
- "@ocfl/ocfl": "0.2.0"
48
+ "@ocfl/ocfl": "0.3.0"
48
49
  },
49
50
  "files": [
50
51
  "index.js",