@zenfs/core 1.7.2 → 1.8.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.
Files changed (68) hide show
  1. package/dist/backends/backend.js +3 -4
  2. package/dist/backends/fetch.d.ts +17 -18
  3. package/dist/backends/fetch.js +95 -58
  4. package/dist/backends/index.d.ts +2 -1
  5. package/dist/backends/index.js +2 -1
  6. package/dist/backends/memory.d.ts +1 -1
  7. package/dist/backends/overlay.d.ts +8 -14
  8. package/dist/backends/overlay.js +38 -31
  9. package/dist/backends/passthrough.d.ts +8 -3
  10. package/dist/backends/passthrough.js +148 -4
  11. package/dist/backends/port/fs.d.ts +15 -49
  12. package/dist/backends/port/fs.js +28 -116
  13. package/dist/backends/port/rpc.d.ts +13 -6
  14. package/dist/backends/port/rpc.js +9 -7
  15. package/dist/backends/store/file_index.d.ts +38 -0
  16. package/dist/backends/store/file_index.js +76 -0
  17. package/dist/backends/store/fs.d.ts +39 -34
  18. package/dist/backends/store/fs.js +407 -238
  19. package/dist/backends/store/index_fs.d.ts +34 -0
  20. package/dist/backends/store/index_fs.js +67 -0
  21. package/dist/backends/store/inode.d.ts +26 -8
  22. package/dist/backends/store/inode.js +92 -91
  23. package/dist/backends/store/simple.d.ts +20 -20
  24. package/dist/backends/store/simple.js +3 -4
  25. package/dist/backends/store/store.d.ts +12 -12
  26. package/dist/backends/store/store.js +4 -6
  27. package/dist/devices.d.ts +44 -21
  28. package/dist/devices.js +110 -55
  29. package/dist/file.d.ts +111 -7
  30. package/dist/file.js +324 -92
  31. package/dist/filesystem.d.ts +44 -4
  32. package/dist/mixins/async.js +12 -6
  33. package/dist/mixins/mutexed.d.ts +8 -3
  34. package/dist/mixins/mutexed.js +57 -1
  35. package/dist/mixins/readonly.d.ts +17 -16
  36. package/dist/mixins/readonly.js +6 -0
  37. package/dist/mixins/sync.d.ts +1 -1
  38. package/dist/stats.d.ts +12 -6
  39. package/dist/stats.js +14 -6
  40. package/dist/utils.d.ts +23 -3
  41. package/dist/utils.js +58 -10
  42. package/dist/vfs/async.js +1 -1
  43. package/dist/vfs/constants.d.ts +2 -2
  44. package/dist/vfs/constants.js +2 -2
  45. package/dist/vfs/dir.js +3 -1
  46. package/dist/vfs/index.js +4 -1
  47. package/dist/vfs/promises.js +33 -13
  48. package/dist/vfs/shared.js +2 -0
  49. package/dist/vfs/sync.js +25 -13
  50. package/dist/vfs/types.d.ts +15 -0
  51. package/eslint.shared.js +1 -0
  52. package/package.json +2 -3
  53. package/readme.md +2 -2
  54. package/scripts/test.js +73 -11
  55. package/tests/common/mutex.test.ts +1 -1
  56. package/tests/fetch/run.sh +16 -0
  57. package/tests/fetch/server.ts +49 -0
  58. package/tests/fetch/setup.ts +13 -0
  59. package/tests/fs/read.test.ts +10 -10
  60. package/tests/fs/times.test.ts +2 -2
  61. package/tests/fs/write.test.ts +6 -11
  62. package/tests/setup/index.ts +38 -0
  63. package/tests/setup/port.ts +15 -0
  64. package/dist/backends/file_index.d.ts +0 -63
  65. package/dist/backends/file_index.js +0 -163
  66. package/tests/common/async.test.ts +0 -31
  67. package/tests/setup/cow+fetch.ts +0 -45
  68. /package/tests/fs/{appendFile.test.ts → append.test.ts} +0 -0
@@ -1,63 +0,0 @@
1
- import { NoSyncFile } from '../file.js';
2
- import { FileSystem } from '../filesystem.js';
3
- import type { StatsLike } from '../stats.js';
4
- import { Stats } from '../stats.js';
5
- /**
6
- * An Index in JSON form
7
- * @internal
8
- */
9
- export interface IndexData {
10
- version: number;
11
- entries: Record<string, StatsLike<number>>;
12
- }
13
- export declare const version = 1;
14
- /**
15
- * An index of files
16
- * @internal
17
- */
18
- export declare class Index extends Map<string, Stats> {
19
- /**
20
- * Convenience method
21
- */
22
- files(): Map<string, Stats>;
23
- /**
24
- * Converts the index to JSON
25
- */
26
- toJSON(): IndexData;
27
- /**
28
- * Converts the index to a string
29
- */
30
- toString(): string;
31
- /**
32
- * Returns the files in the directory `dir`.
33
- * This is expensive so it is only called once per directory.
34
- */
35
- protected dirEntries(dir: string): string[];
36
- /**
37
- * Loads the index from JSON data
38
- */
39
- fromJSON(json: IndexData): void;
40
- /**
41
- * Parses an index from a string
42
- */
43
- static parse(data: string): Index;
44
- }
45
- declare const IndexFS_base: import("../index.js").Mixin<typeof FileSystem, import("../mixins/readonly.js").ReadonlyMixin>;
46
- export declare abstract class IndexFS extends IndexFS_base {
47
- private indexData;
48
- protected index: Index;
49
- protected _isInitialized: boolean;
50
- ready(): Promise<void>;
51
- constructor(indexData: IndexData | Promise<IndexData>);
52
- reloadFiles(): Promise<void>;
53
- reloadFilesSync(): void;
54
- stat(path: string): Promise<Stats>;
55
- statSync(path: string): Stats;
56
- openFile(path: string, flag: string): Promise<NoSyncFile<this>>;
57
- openFileSync(path: string, flag: string): NoSyncFile<this>;
58
- readdir(path: string): Promise<string[]>;
59
- readdirSync(path: string): string[];
60
- protected abstract getData(path: string, stats: Stats): Promise<Uint8Array>;
61
- protected abstract getDataSync(path: string, stats: Stats): Uint8Array;
62
- }
63
- export {};
@@ -1,163 +0,0 @@
1
- /* Note: this file is named file_index.ts because Typescript has special behavior regarding index.ts which can't be disabled. */
2
- import { isJSON } from 'utilium';
3
- import { Errno, ErrnoError } from '../error.js';
4
- import { NoSyncFile, isWriteable } from '../file.js';
5
- import { FileSystem } from '../filesystem.js';
6
- import { Readonly } from '../mixins/readonly.js';
7
- import { Stats } from '../stats.js';
8
- import { decodeUTF8, encodeUTF8 } from '../utils.js';
9
- import { basename, dirname } from '../vfs/path.js';
10
- export const version = 1;
11
- /**
12
- * An index of files
13
- * @internal
14
- */
15
- export class Index extends Map {
16
- /**
17
- * Convenience method
18
- */
19
- files() {
20
- const files = new Map();
21
- for (const [path, stats] of this) {
22
- if (stats.isFile()) {
23
- files.set(path, stats);
24
- }
25
- }
26
- return files;
27
- }
28
- /**
29
- * Converts the index to JSON
30
- */
31
- toJSON() {
32
- return {
33
- version,
34
- entries: Object.fromEntries(this),
35
- };
36
- }
37
- /**
38
- * Converts the index to a string
39
- */
40
- toString() {
41
- return JSON.stringify(this.toJSON());
42
- }
43
- /**
44
- * Returns the files in the directory `dir`.
45
- * This is expensive so it is only called once per directory.
46
- */
47
- dirEntries(dir) {
48
- const entries = [];
49
- for (const entry of this.keys()) {
50
- if (dirname(entry) == dir) {
51
- entries.push(basename(entry));
52
- }
53
- }
54
- return entries;
55
- }
56
- /**
57
- * Loads the index from JSON data
58
- */
59
- fromJSON(json) {
60
- if (json.version != version) {
61
- throw new ErrnoError(Errno.EINVAL, 'Index version mismatch');
62
- }
63
- this.clear();
64
- for (const [path, data] of Object.entries(json.entries)) {
65
- const stats = new Stats(data);
66
- if (stats.isDirectory()) {
67
- stats.fileData = encodeUTF8(JSON.stringify(this.dirEntries(path)));
68
- }
69
- this.set(path, stats);
70
- }
71
- }
72
- /**
73
- * Parses an index from a string
74
- */
75
- static parse(data) {
76
- if (!isJSON(data)) {
77
- throw new ErrnoError(Errno.EINVAL, 'Invalid JSON');
78
- }
79
- const json = JSON.parse(data);
80
- const index = new Index();
81
- index.fromJSON(json);
82
- return index;
83
- }
84
- }
85
- export class IndexFS extends Readonly(FileSystem) {
86
- async ready() {
87
- await super.ready();
88
- if (this._isInitialized) {
89
- return;
90
- }
91
- this.index.fromJSON(await this.indexData);
92
- this._isInitialized = true;
93
- }
94
- constructor(indexData) {
95
- super();
96
- this.indexData = indexData;
97
- this.index = new Index();
98
- this._isInitialized = false;
99
- }
100
- async reloadFiles() {
101
- for (const [path, stats] of this.index.files()) {
102
- delete stats.fileData;
103
- stats.fileData = await this.getData(path, stats);
104
- }
105
- }
106
- reloadFilesSync() {
107
- for (const [path, stats] of this.index.files()) {
108
- delete stats.fileData;
109
- stats.fileData = this.getDataSync(path, stats);
110
- }
111
- }
112
- stat(path) {
113
- return Promise.resolve(this.statSync(path));
114
- }
115
- statSync(path) {
116
- if (!this.index.has(path)) {
117
- throw ErrnoError.With('ENOENT', path, 'stat');
118
- }
119
- return this.index.get(path);
120
- }
121
- async openFile(path, flag) {
122
- if (isWriteable(flag)) {
123
- // You can't write to files on this file system.
124
- throw new ErrnoError(Errno.EPERM, path);
125
- }
126
- // Check if the path exists, and is a file.
127
- const stats = this.index.get(path);
128
- if (!stats) {
129
- throw ErrnoError.With('ENOENT', path, 'openFile');
130
- }
131
- return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : await this.getData(path, stats));
132
- }
133
- openFileSync(path, flag) {
134
- if (isWriteable(flag)) {
135
- // You can't write to files on this file system.
136
- throw new ErrnoError(Errno.EPERM, path);
137
- }
138
- // Check if the path exists, and is a file.
139
- const stats = this.index.get(path);
140
- if (!stats) {
141
- throw ErrnoError.With('ENOENT', path, 'openFile');
142
- }
143
- return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.getDataSync(path, stats));
144
- }
145
- readdir(path) {
146
- return Promise.resolve(this.readdirSync(path));
147
- }
148
- readdirSync(path) {
149
- // Check if it exists.
150
- const stats = this.index.get(path);
151
- if (!stats) {
152
- throw ErrnoError.With('ENOENT', path, 'readdir');
153
- }
154
- const content = JSON.parse(decodeUTF8(stats.fileData));
155
- if (!Array.isArray(content)) {
156
- throw ErrnoError.With('ENODATA', path, 'readdir');
157
- }
158
- if (!content.every(item => typeof item == 'string')) {
159
- throw ErrnoError.With('ENODATA', path, 'readdir');
160
- }
161
- return content;
162
- }
163
- }
@@ -1,31 +0,0 @@
1
- import assert from 'node:assert';
2
- import { suite, test } from 'node:test';
3
- import { configure } from '../../dist/config.js';
4
- import * as fs from '../../dist/vfs/index.js';
5
- import { InMemoryStore, StoreFS } from '../../dist/index.js';
6
- import { Async } from '../../dist/mixins/async.js';
7
-
8
- class ExampleAsyncFS extends Async(StoreFS) {
9
- _sync = new StoreFS(new InMemoryStore('cache'));
10
-
11
- public constructor() {
12
- super(new InMemoryStore('test'));
13
- }
14
- }
15
-
16
- const asyncFS = new ExampleAsyncFS();
17
-
18
- await configure({ mounts: { '/': asyncFS } });
19
-
20
- suite('Async Mixin', () => {
21
- test('async -> cache syncing', async () => {
22
- await fs.promises.writeFile('test', 'test');
23
- assert.strictEqual(fs.readFileSync('test', 'utf8'), 'test');
24
- });
25
-
26
- test('cache -> async syncing', async () => {
27
- fs.unlinkSync('test');
28
- await asyncFS.queueDone();
29
- assert(!(await fs.promises.exists('test')));
30
- });
31
- });
@@ -1,45 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
- import { readFileSync } from 'node:fs';
3
- import { createServer } from 'node:http';
4
- import { join } from 'node:path';
5
- import { configureSingle, Fetch, InMemory, Overlay } from '../../dist/index.js';
6
- import { data, tmp } from '../setup.js';
7
-
8
- const port = 26514,
9
- index = tmp + '/index.json';
10
-
11
- const statusCodes = {
12
- ENOENT: 404,
13
- };
14
-
15
- execSync(`npm exec make-index -- ${data} --output ${index} --quiet`, { stdio: 'inherit' });
16
-
17
- const server = createServer((request, response) => {
18
- const path = request.url == '/.index.json' ? index : join(data, request.url?.slice(1) || '');
19
- try {
20
- response.statusCode = 200;
21
- response.end(readFileSync(path));
22
- } catch (e: any) {
23
- response.statusCode = statusCodes[e.code as keyof typeof statusCodes] || 400;
24
- response.end();
25
- }
26
- });
27
-
28
- server
29
- .once('error', (error: NodeJS.ErrnoException) => {
30
- if (error.code == 'EADDRINUSE') return;
31
- throw error;
32
- })
33
- .listen(port)
34
- .unref();
35
-
36
- const baseUrl = 'http://localhost:' + port;
37
-
38
- await configureSingle({
39
- backend: Overlay,
40
- readable: Fetch.create({
41
- baseUrl,
42
- index: baseUrl + '/.index.json',
43
- }),
44
- writable: InMemory.create({ name: 'cow' }),
45
- });
File without changes