@themartiancompany/opfs 1.8.11

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.
Files changed (114) hide show
  1. package/COPYING +674 -0
  2. package/README.cn.md +301 -0
  3. package/README.md +241 -0
  4. package/dist/main.cjs +1840 -0
  5. package/dist/main.cjs.map +1 -0
  6. package/dist/main.mjs +1751 -0
  7. package/dist/main.mjs.map +1 -0
  8. package/dist/types.d.ts +920 -0
  9. package/dist/types.d.ts.map +1 -0
  10. package/docs/README.md +115 -0
  11. package/docs/classes/SyncMessenger.md +42 -0
  12. package/docs/functions/appendFile.md +29 -0
  13. package/docs/functions/appendFileSync.md +26 -0
  14. package/docs/functions/assertAbsolutePath.md +29 -0
  15. package/docs/functions/assertFileUrl.md +29 -0
  16. package/docs/functions/connectSyncAgent.md +25 -0
  17. package/docs/functions/copy.md +35 -0
  18. package/docs/functions/copySync.md +30 -0
  19. package/docs/functions/createFile.md +27 -0
  20. package/docs/functions/createFileSync.md +25 -0
  21. package/docs/functions/deleteTemp.md +23 -0
  22. package/docs/functions/deleteTempSync.md +19 -0
  23. package/docs/functions/downloadFile.md +58 -0
  24. package/docs/functions/emptyDir.md +28 -0
  25. package/docs/functions/emptyDirSync.md +25 -0
  26. package/docs/functions/exists.md +29 -0
  27. package/docs/functions/existsSync.md +26 -0
  28. package/docs/functions/generateTempPath.md +27 -0
  29. package/docs/functions/getFileDataByHandle.md +27 -0
  30. package/docs/functions/getSyncMessenger.md +22 -0
  31. package/docs/functions/isDirectoryHandle.md +27 -0
  32. package/docs/functions/isFileHandle.md +27 -0
  33. package/docs/functions/isFileHandleLike.md +27 -0
  34. package/docs/functions/isOPFSSupported.md +21 -0
  35. package/docs/functions/isTempPath.md +27 -0
  36. package/docs/functions/mkTemp.md +28 -0
  37. package/docs/functions/mkTempSync.md +25 -0
  38. package/docs/functions/mkdir.md +27 -0
  39. package/docs/functions/mkdirSync.md +25 -0
  40. package/docs/functions/move.md +34 -0
  41. package/docs/functions/moveSync.md +30 -0
  42. package/docs/functions/pruneTemp.md +28 -0
  43. package/docs/functions/pruneTempSync.md +25 -0
  44. package/docs/functions/readBlobFile.md +28 -0
  45. package/docs/functions/readBlobFileSync.md +25 -0
  46. package/docs/functions/readDir.md +28 -0
  47. package/docs/functions/readDirSync.md +26 -0
  48. package/docs/functions/readFile.md +132 -0
  49. package/docs/functions/readFileSync.md +70 -0
  50. package/docs/functions/readJsonFile.md +35 -0
  51. package/docs/functions/readJsonFileSync.md +31 -0
  52. package/docs/functions/readTextFile.md +28 -0
  53. package/docs/functions/readTextFileSync.md +25 -0
  54. package/docs/functions/remove.md +27 -0
  55. package/docs/functions/removeSync.md +25 -0
  56. package/docs/functions/setSyncMessenger.md +26 -0
  57. package/docs/functions/startSyncAgent.md +21 -0
  58. package/docs/functions/stat.md +27 -0
  59. package/docs/functions/statSync.md +25 -0
  60. package/docs/functions/toFileSystemHandleLike.md +27 -0
  61. package/docs/functions/unzip.md +32 -0
  62. package/docs/functions/unzipFromUrl.md +36 -0
  63. package/docs/functions/unzipSync.md +26 -0
  64. package/docs/functions/uploadFile.md +33 -0
  65. package/docs/functions/writeFile.md +32 -0
  66. package/docs/functions/writeFileSync.md +30 -0
  67. package/docs/functions/zip.md +65 -0
  68. package/docs/functions/zipFromUrl.md +63 -0
  69. package/docs/functions/zipSync.md +55 -0
  70. package/docs/interfaces/CopyOptions.md +17 -0
  71. package/docs/interfaces/DownloadFileTempResponse.md +18 -0
  72. package/docs/interfaces/ErrorLike.md +18 -0
  73. package/docs/interfaces/ExistsOptions.md +18 -0
  74. package/docs/interfaces/FileLike.md +21 -0
  75. package/docs/interfaces/FileSystemFileHandleLike.md +25 -0
  76. package/docs/interfaces/FileSystemHandleLike.md +22 -0
  77. package/docs/interfaces/MoveOptions.md +17 -0
  78. package/docs/interfaces/ReadDirEntry.md +18 -0
  79. package/docs/interfaces/ReadDirEntrySync.md +18 -0
  80. package/docs/interfaces/ReadDirOptions.md +17 -0
  81. package/docs/interfaces/ReadOptions.md +17 -0
  82. package/docs/interfaces/SyncAgentOptions.md +19 -0
  83. package/docs/interfaces/TempOptions.md +19 -0
  84. package/docs/interfaces/UploadRequestInit.md +21 -0
  85. package/docs/interfaces/WriteOptions.md +19 -0
  86. package/docs/interfaces/ZipOptions.md +17 -0
  87. package/docs/type-aliases/FileEncoding.md +15 -0
  88. package/docs/type-aliases/FsRequestInit.md +15 -0
  89. package/docs/type-aliases/ReadFileContent.md +15 -0
  90. package/docs/type-aliases/WriteFileContent.md +15 -0
  91. package/docs/type-aliases/WriteSyncFileContent.md +16 -0
  92. package/docs/variables/CURRENT_DIR.md +15 -0
  93. package/docs/variables/NOT_FOUND_ERROR.md +18 -0
  94. package/docs/variables/ROOT_DIR.md +15 -0
  95. package/docs/variables/TMP_DIR.md +15 -0
  96. package/package.json +141 -0
  97. package/src/fs/assertions.ts +63 -0
  98. package/src/fs/constants.ts +63 -0
  99. package/src/fs/defines.ts +352 -0
  100. package/src/fs/helpers.ts +338 -0
  101. package/src/fs/opfs_core.ts +413 -0
  102. package/src/fs/opfs_download.ts +174 -0
  103. package/src/fs/opfs_ext.ts +504 -0
  104. package/src/fs/opfs_tmp.ts +131 -0
  105. package/src/fs/opfs_unzip.ts +168 -0
  106. package/src/fs/opfs_upload.ts +126 -0
  107. package/src/fs/opfs_zip.ts +314 -0
  108. package/src/fs/support.ts +36 -0
  109. package/src/fs/utils.ts +176 -0
  110. package/src/mod.ts +41 -0
  111. package/src/worker/helpers.ts +168 -0
  112. package/src/worker/opfs_worker.ts +298 -0
  113. package/src/worker/opfs_worker_adapter.ts +666 -0
  114. package/src/worker/shared.ts +400 -0
package/README.cn.md ADDED
@@ -0,0 +1,301 @@
1
+ [comment]: <> (SPDX-License-Identifier: AGPL-3.0)
2
+
3
+ [comment]: <> (-------------------------------------------------------------)
4
+ [comment]: <> (Copyright © 2024, 2025 Pellegrino Prevete)
5
+ [comment]: <> (All rights reserved)
6
+ [comment]: <> (-------------------------------------------------------------)
7
+
8
+ [comment]: <> (This program is free software: you can redistribute)
9
+ [comment]: <> (it and/or modify it under the terms of the GNU Affero)
10
+ [comment]: <> (General Public License as published by the Free)
11
+ [comment]: <> (Software Foundation, either version 3 of the License.)
12
+
13
+ [comment]: <> (This program is distributed in the hope that it will be useful,)
14
+ [comment]: <> (but WITHOUT ANY WARRANTY; without even the implied warranty of)
15
+ [comment]: <> (MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the)
16
+ [comment]: <> (GNU Affero General Public License for more details.)
17
+
18
+ [comment]: <> (You should have received a copy of the GNU Affero General Public)
19
+ [comment]: <> (License along with this program.)
20
+ [comment]: <> (If not, see <https://www.gnu.org/licenses/>.)
21
+
22
+
23
+ # 快乐地使用 OPFS
24
+
25
+ [![NPM version](
26
+ http://img.shields.io/npm/v/@themartiancompany/opfs.svg)](
27
+ https://npmjs.org/package/@themartiancompany/opfs)
28
+
29
+ [![en](
30
+ https://img.shields.io/badge/lang-en-black.svg)](
31
+ README.md)
32
+
33
+
34
+ 这是一套参考 [Deno Runtime File System](
35
+ https://deno.land/api#File_System)
36
+ 和 [Deno @std/fs](
37
+ https://jsr.io/@std/fs)
38
+ API,基于 OPFS 实现的浏览器可用的 fs 模块。
39
+
40
+ ## 安装
41
+
42
+ ```sh
43
+ npm \
44
+ install \
45
+ --save \
46
+ "@themartiancompany/opfs"
47
+ ```
48
+
49
+ ## 什么是 OPFS
50
+
51
+ OPFS 是 [Origin private file system](
52
+ https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system)
53
+ 的简称,旨在为浏览器环境提供一套文件系统 API 来操作本地文件。
54
+
55
+ ## 为什么会有 'opfs`
56
+
57
+ 标准的 OPFS API 和我们熟知的基于路径操作的文件系统 API 如 Node.js、Deno
58
+ 存在较大的区别,本项目即是为了实现在浏览器中也能拥有 Deno
59
+ 一样的 API 方便地操作文件而生。
60
+
61
+ 异步 API 的返回值都是 [Result](
62
+ https://github.com/JiangJie/happy-rusty)
63
+ 类型,类似 Rust 的 `Result`
64
+ 枚举类型,提供更友好的错误处理方式。
65
+
66
+ ## 为什么参考 Deno 而不是 Node.js
67
+
68
+ - 早期的 Node.js fs API
69
+ 是基于回调的语法,虽然较新的版本支持了 Promise
70
+ 语法,而 Deno fs API 则一开始就是基于 Promise
71
+ 语法,这样来说的话,Deno 有更少的历史包袱,要实现和 Native
72
+ 兼容的 API,选择 Deno 做为参考显然更合适。
73
+ - Deno 原生支持 TypeScript,而 Node.js
74
+ 在不借助于其它工具的情况下暂不支持。
75
+
76
+ ## 是否支持同步接口
77
+
78
+ **支持**
79
+
80
+ > [!NOTE]
81
+ 但更推荐使用异步接口,因为主线程并未提供同步接口,为了强制实现同步语法,需要将 I/O 操作移到 `Worker` 进行,同时主线程需要处于阻塞状态,直到 `Worker` 完成 I/O 操作,这显然会带来性能上的损失。
82
+
83
+ 并且由于需要启动 `Worker,同步接口需要在`
84
+ `Worker` 启动后才能使用,在此之前任何读写都会失败。
85
+
86
+ **请注意**,为了在主线程和 `Worker`
87
+ 之间共享数据,需要使用 `SharedArrayBuffer`,为此需要两个额外的 `HTTP Response Header`:
88
+ `'Cross-Origin-Opener-Policy': 'same-origin'`
89
+ `'Cross-Origin-Embedder-Policy': 'require-corp'`。
90
+ 否则会报错 `ReferenceError: SharedArrayBuffer is not defined`。
91
+
92
+ ## 示例
93
+
94
+ ```ts
95
+ import * as fs from 'opfs';
96
+
97
+ (async () => {
98
+ const mockServer = 'https://16a6dafa-2258-4a83-88fa-31a409e42b17.mock.pstmn.io';
99
+ const mockTodos = `${ mockServer }/todos`;
100
+ const mockTodo1 = `${ mockTodos }/1`;
101
+
102
+ // Check if OPFS is supported
103
+ console.log(`OPFS is${ isOPFSSupported() ? '' : ' not' } supported`);
104
+
105
+ // Clear all files and folders
106
+ await fs.emptyDir(fs.ROOT_DIR);
107
+ // Recursively create the /happy/opfs directory
108
+ await fs.mkdir('/happy/opfs');
109
+ // Create and write file content
110
+ await fs.writeFile('/happy/opfs/a.txt', 'hello opfs');
111
+ await fs.writeFile('/happy/op-fs/fs.txt', 'hello opfs');
112
+ // Move the file
113
+ await fs.move('/happy/opfs/a.txt', '/happy/b.txt');
114
+ // Append content to the file
115
+ await fs.appendFile('/happy/b.txt', new TextEncoder().encode(' happy opfs'));
116
+
117
+ // File no longer exists
118
+ const statRes = await fs.stat('/happy/opfs/a.txt');
119
+ console.assert(statRes.isErr());
120
+
121
+ console.assert((await fs.readFile('/happy/b.txt')).unwrap().byteLength === 21);
122
+ // Automatically normalize the path
123
+ console.assert((await fs.readTextFile('//happy///b.txt//')).unwrap() === 'hello opfs happy opfs');
124
+
125
+ console.assert((await fs.remove('/happy/not/exists')).isOk());
126
+ console.assert((await fs.remove('/happy/opfs')).isOk());
127
+
128
+ console.assert(!(await fs.exists('/happy/opfs')).unwrap());
129
+ console.assert((await fs.exists('/happy/b.txt')).unwrap());
130
+ console.assert(fs.isFileHandle((await fs.stat('/happy/b.txt')).unwrap()));
131
+
132
+ // Download a file
133
+ const downloadTask = fs.downloadFile(mockSingle, '/todo.json', {
134
+ timeout: 1000,
135
+ onProgress(progressResult): void {
136
+ progressResult.inspect(progress => {
137
+ console.log(`Downloaded ${ progress.completedByteLength }/${ progress.totalByteLength } bytes`);
138
+ });
139
+ },
140
+ });
141
+ const downloadRes = await downloadTask.response;
142
+ if (downloadRes.isOk()) {
143
+ console.assert(downloadRes.unwrap() instanceof Response);
144
+
145
+ const postData = (await fs.readTextFile('/todo.json')).unwrap();
146
+ const postJson: {
147
+ id: number;
148
+ title: string;
149
+ } = JSON.parse(postData);
150
+ console.assert(postJson.id === 1);
151
+
152
+ // Modify the file
153
+ postJson.title = 'happy-opfs';
154
+ await fs.writeFile('/todo.json', JSON.stringify(postJson));
155
+
156
+ // Upload a file
157
+ console.assert((await fs.uploadFile('/todo.json', mockAll).response).unwrap() instanceof Response);
158
+ } else {
159
+ console.assert(downloadRes.unwrapErr() instanceof Error);
160
+ }
161
+
162
+ {
163
+ // Download a file to a temporary file
164
+ const downloadTask = fs.downloadFile(mockSingle);
165
+ const downloadRes = await downloadTask.response;
166
+ downloadRes.inspect(x => {
167
+ console.assert(fs.isTempPath(x.tempFilePath));
168
+ console.assert(x.rawResponse instanceof Response);
169
+ });
170
+ if (downloadRes.isOk()) {
171
+ await fs.remove(downloadRes.unwrap().tempFilePath);
172
+ }
173
+ }
174
+
175
+ // Will create directory
176
+ await fs.emptyDir('/not-exists');
177
+
178
+ // Zip/Unzip
179
+ console.assert((await fs.zip('/happy', '/happy.zip')).isOk());
180
+ console.assert((await fs.zip('/happy')).unwrap().byteLength === (await fs.readFile('/happy.zip')).unwrap().byteLength);
181
+ console.assert((await fs.unzip('/happy.zip', '/happy-2')).isOk());
182
+ console.assert((await fs.unzipFromUrl(mockZipUrl, '/happy-3', {
183
+ onProgress(progressResult) {
184
+ progressResult.inspect(progress => {
185
+ console.log(`Unzipped ${ progress.completedByteLength }/${ progress.totalByteLength } bytes`);
186
+ });
187
+ },
188
+ })).isOk());
189
+ console.assert((await fs.zipFromUrl(mockZipUrl, '/test-zip.zip')).isOk());
190
+ console.assert((await fs.zipFromUrl(mockZipUrl)).unwrap().byteLength === (await fs.readFile('/test-zip.zip')).unwrap().byteLength);
191
+
192
+ // Temp
193
+ console.log(`temp txt file: ${ fs.generateTempPath({
194
+ basename: 'opfs',
195
+ extname: '.txt',
196
+ }) }`);
197
+ console.log(`temp dir: ${ fs.generateTempPath({
198
+ isDirectory: true,
199
+ }) }`);
200
+ (await fs.mkTemp()).inspect(path => {
201
+ console.assert(path.startsWith('/tmp/tmp-'));
202
+ });
203
+ const expired = new Date();
204
+ (await fs.mkTemp({
205
+ basename: 'opfs',
206
+ extname: '.txt',
207
+ })).inspect(path => {
208
+ console.assert(path.startsWith('/tmp/opfs-'));
209
+ console.assert(path.endsWith('.txt'));
210
+ });
211
+ (await fs.mkTemp({
212
+ isDirectory: true,
213
+ basename: '',
214
+ })).inspect(path => {
215
+ console.assert(path.startsWith('/tmp/'));
216
+ });
217
+ console.assert((await Array.fromAsync((await fs.readDir(fs.TMP_DIR)).unwrap())).length === 3);
218
+ await fs.pruneTemp(expired);
219
+ console.assert((await Array.fromAsync((await fs.readDir(fs.TMP_DIR)).unwrap())).length === 2);
220
+ // await fs.deleteTemp();
221
+ // console.assert(!(await fs.exists(fs.TMP_DIR)).unwrap());
222
+
223
+ // Copy
224
+ await fs.mkdir('/happy/copy');
225
+ console.assert((await fs.copy('/happy/b.txt', '/happy-2')).isErr());
226
+ console.assert((await fs.copy('/happy', '/happy-copy')).isOk());
227
+ await fs.appendFile('/happy-copy/b.txt', ' copy');
228
+ console.assert((await fs.readFile('/happy-copy/b.txt')).unwrap().byteLength === 26);
229
+ await fs.appendFile('/happy/op-fs/fs.txt', ' copy');
230
+ await fs.copy('/happy', '/happy-copy', {
231
+ overwrite: false,
232
+ });
233
+ console.assert((await fs.readFile('/happy-copy/b.txt')).unwrap().byteLength === 26);
234
+
235
+ // List all files and folders in the root directory
236
+ for await (const { path, handle } of (await fs.readDir(fs.ROOT_DIR, {
237
+ recursive: true,
238
+ })).unwrap()) {
239
+ const handleLike = await fs.toFileSystemHandleLike(handle);
240
+ if (fs.isFileHandleLike(handleLike)) {
241
+ console.log(`${ path } is a ${ handleLike.kind }, name = ${ handleLike.name }, type = ${ handleLike.type }, size = ${ handleLike.size }, lastModified = ${ handleLike.lastModified }`);
242
+ } else {
243
+ console.log(`${ path } is a ${ handleLike.kind }, name = ${ handleLike.name }`);
244
+ }
245
+ }
246
+
247
+ // Comment this line to view using OPFS Explorer
248
+ await fs.remove(fs.ROOT_DIR);
249
+ })();
250
+ ```
251
+
252
+ 以上示例代码可以在 [tests/async.ts](
253
+ tests/async.ts)
254
+ 找到,也可以通过以下方式查看运行时效果。
255
+
256
+ ```
257
+ git clone https://github.com/themartiancompany/opfs.git
258
+ cd opfs
259
+ pnpm install
260
+ pnpm start
261
+ ```
262
+
263
+ 通过浏览器打开 [https://localhost:8443/](
264
+ https://localhost:8443/),打开开发者工具观察 console
265
+ 的输出。
266
+
267
+ 你也可以安装 [OPFS Explorer](
268
+ https://chromewebstore.google.com/detail/acndjpgkpaclldomagafnognkcgjignd)
269
+ 浏览器扩展,以便直观地查看文件系统状态。
270
+
271
+ ### 同步示例
272
+
273
+ `worker.ts`
274
+ ```ts
275
+ import { startSyncAgent } from 'happy-opfs';
276
+
277
+ startSyncAgent();
278
+ ```
279
+
280
+ `index.ts`
281
+ ```ts
282
+ import { connectSyncAgent, mkdirSync } from 'happy-opfs';
283
+
284
+ await connectSyncAgent({
285
+ worker: new Worker(new URL('worker.ts', import.meta.url), {
286
+ type: 'module'
287
+ }),
288
+ // SharedArrayBuffer size between main thread and worker
289
+ bufferLength: 10 * 1024 * 1024,
290
+ // max wait time at main thread per operation
291
+ opTimeout: 3000,
292
+ });
293
+
294
+ mkdirSync('/happy/opfs');
295
+ // other sync operations
296
+ ```
297
+
298
+ 详见 [tests/sync.ts](
299
+ tests/sync.ts)。
300
+
301
+ ## [文档](docs/README.md)
package/README.md ADDED
@@ -0,0 +1,241 @@
1
+ [comment]: <> (SPDX-License-Identifier: AGPL-3.0)
2
+
3
+ [comment]: <> (-------------------------------------------------------------)
4
+ [comment]: <> (Copyright © 2024, 2025 Pellegrino Prevete)
5
+ [comment]: <> (All rights reserved)
6
+ [comment]: <> (-------------------------------------------------------------)
7
+
8
+ [comment]: <> (This program is free software: you can redistribute)
9
+ [comment]: <> (it and/or modify it under the terms of the GNU Affero)
10
+ [comment]: <> (General Public License as published by the Free)
11
+ [comment]: <> (Software Foundation, either version 3 of the License.)
12
+
13
+ [comment]: <> (This program is distributed in the hope that it will be useful,)
14
+ [comment]: <> (but WITHOUT ANY WARRANTY; without even the implied warranty of)
15
+ [comment]: <> (MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the)
16
+ [comment]: <> (GNU Affero General Public License for more details.)
17
+
18
+ [comment]: <> (You should have received a copy of the GNU Affero General Public)
19
+ [comment]: <> (License along with this program.)
20
+ [comment]: <> (If not, see <https://www.gnu.org/licenses/>.)
21
+
22
+ # Origin Private File System module (`@themartiancompany/opfs`)
23
+
24
+ [![NPM version](
25
+ https://img.shields.io/npm/v/@themartiancompany/opfs.svg)](
26
+ https://npmjs.org/package/@themartiancompany/opfs)
27
+
28
+ [![cn](
29
+ https://img.shields.io/badge/lang-cn-red.svg)](
30
+ README.cn.md)
31
+
32
+ Browser-compatible `fs` module leveraging the
33
+ [Origin Private File System](
34
+ https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system)
35
+ (OPFS) specification which references the
36
+ [Deno Runtime File System](
37
+ https://deno.land/api#File_System) and
38
+ [Deno `@std/fs`](
39
+ https://jsr.io/@std/fs)
40
+ APIs.
41
+
42
+ OPFS stands for *origin private file system*
43
+ and it is a file system API for manipulating local
44
+ files in a browser environment.
45
+
46
+ There are significant differences between the
47
+ standard OPFS API and familiar file system APIs
48
+ based on path operations, such as those of
49
+ Node.js and Deno.
50
+ The purpose of this module is to implement an API
51
+ similar to those in the browser, allowing for
52
+ convenient file operations.
53
+
54
+ The return values of asynchronous APIs are of the
55
+ [Result](
56
+ https://github.com/JiangJie/happy-rusty)
57
+ type, similar to Rust's `Result` enum type,
58
+ providing a more user-friendly error handling approach.
59
+
60
+ As to why the library targets Deno, that's because:
61
+
62
+ - early versions of the Node.js fs API were based
63
+ on callback syntax, although newer versions support
64
+ Promise syntax;
65
+ on the other hand, the Deno fs API was designed from
66
+ the beginning with Promise syntax. Therefore, Deno has
67
+ less historical baggage, making it a more suitable choice
68
+ for implementing a native-compatible API;
69
+ - Deno natively supports TypeScript, while Node.js
70
+ currently does not without the use of additional tools.
71
+
72
+ Originally based on the
73
+ [Happy OPFS](
74
+ https://github.com/JiangJie/happy-opfs)
75
+ module by Jian Jie and renamed
76
+ because I've noticed there was no
77
+ `fs.createReadStream` implementation,
78
+ while the other available
79
+ [OPFS Tools](
80
+ https://github.com/hughfenghen/opfs-tools)
81
+ module had but didn't call it verbatim
82
+ so I've thought it may have
83
+ been appropriate to make everybody including
84
+ the module potentially less happy and check
85
+ whether if a third module including the function
86
+ and simply called `opfs` can show how much the
87
+ `namespace/package` model works on the field.
88
+
89
+ ## Installation
90
+
91
+ To install a locally built version of the
92
+ library run
93
+
94
+ ```bash
95
+ $ make \
96
+ all
97
+ ```
98
+
99
+ An `opfs-<version>.tgz` npm archive will be generated
100
+ in the root of the repository.
101
+
102
+ To install the library system-wide run
103
+
104
+ ```bash
105
+ # make \
106
+ install-npm
107
+ ```
108
+
109
+ To install it as a
110
+ [DogeOS](
111
+ https://githubcom/themartiancompany/dogeos)
112
+ system package from the
113
+ [Ur](
114
+ https://github.com/themartiancompany/ur)
115
+ uncensorable user repository and application
116
+ store run
117
+
118
+ ```bash
119
+ ur \
120
+ "nodejs-opfs"
121
+ ```
122
+
123
+ A mirror of the Ur universal recipe
124
+ has been made available on The Martian
125
+ Company's Github at
126
+ [`nodejs-opfs-ur`](
127
+ https://github.com/themartiancompany/nodejs-opfs-ur).
128
+
129
+ To download the library from
130
+ the NPM Registry run
131
+
132
+ ```bash
133
+ npm \
134
+ install \
135
+ --save \
136
+ "@themartiancompany/opfs"
137
+ ```
138
+
139
+ ## Synchronous support
140
+
141
+ > [!NOTE]
142
+ The asynchronous interface is to be preferred
143
+ because the main thread does not provide a synchronous interface,
144
+ so in order to force the implementation of synchronous syntax,
145
+ the I/O operation needs to be moved a
146
+ [`Worker`](
147
+ https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API),
148
+ and the main thread needs to be blocked until the
149
+ `Worker` completes its I/O operation, which obviously
150
+ causes performance loss.
151
+
152
+ Also since the `Worker` needs to be started first
153
+ the synchronous interface can only be used after start completion,
154
+ such that any reading and writing before that will fail.
155
+
156
+ **Please note** that in order to share data
157
+ between the main thread and the `Worker`,
158
+ `SharedArrayBuffer` needs to be used and so
159
+ two additional `HTTP Response Headers` are required for this:
160
+ `'Cross-Origin-Opener-Policy': 'same-origin'`
161
+ and
162
+ `'Cross-Origin-Embedder-Policy': 'require-corp'`,
163
+ otherwise a `ReferenceError: SharedArrayBuffer is not defined`
164
+ error will be thrown.
165
+
166
+ The headers are automatically set up respectively by
167
+ Parcel and Serve in the Typescript and
168
+ Javascript examples below.
169
+
170
+ ## Examples
171
+
172
+ To visually inspect file system status in-browser
173
+ it is available the
174
+ [OPFS Explorer](
175
+ https://chromewebstore.google.com/detail/acndjpgkpaclldomagafnognkcgjignd)
176
+ extension.
177
+
178
+ ### Javascript
179
+
180
+ If you are looking for an example of how
181
+ to write a full computer application
182
+ composed of only Bash programs and
183
+ Node modules but which runs the
184
+ same and with zero modifications
185
+ regardless of whether it has available
186
+ for display a terminal or a browser window,
187
+ you can look at the
188
+ [Ahsi](
189
+ https://github.com/themartiancompany/ahsi)
190
+ program, written using the
191
+ [Crash Javascript](
192
+ https://github.com/themartiancompany/crash-js)
193
+ and
194
+ [Crash Bash](
195
+ https://github.com/themartiancompany/crash-bash)
196
+ libraries.
197
+
198
+ Refer to the Ahsi repository for more information
199
+ about installing and running it.
200
+
201
+ ### Typescript
202
+
203
+ Typescript asynchronous
204
+ ([tests/async.ts](
205
+ tests/async.ts))
206
+ and synchronous examples
207
+ ([tests/sync.ts](
208
+ tests/sync.ts))
209
+ are made available in the `tests` directory.
210
+
211
+ To build them and make them available in an
212
+ HTTP server run
213
+
214
+ ```bash
215
+ npm \
216
+ run \
217
+ start
218
+ ```
219
+
220
+ then open
221
+ [https://localhost:8443/](
222
+ https://localhost:8443/)
223
+ in your browser and open the developer
224
+ tools to observe the console output.
225
+
226
+ ## Documentation
227
+
228
+ Module API is in the
229
+ [docs](
230
+ docs/README.md)
231
+ directory.
232
+
233
+ ## License
234
+
235
+ This software repository by Jian Jie
236
+ and Pellegrino Prevete is released
237
+ under the terms of the GNU Affero
238
+ General Public License version 3.
239
+ Portions of the works authored by
240
+ Jian are released under GNU General
241
+ Public License version 3.