@zenfs/core 1.1.6 → 1.2.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.
Files changed (58) hide show
  1. package/dist/backends/file_index.js +0 -3
  2. package/dist/backends/overlay.js +0 -8
  3. package/dist/backends/store/fs.js +4 -17
  4. package/dist/config.d.ts +14 -0
  5. package/dist/config.js +4 -0
  6. package/dist/devices.js +0 -12
  7. package/dist/emulation/cache.d.ts +21 -0
  8. package/dist/emulation/cache.js +36 -0
  9. package/dist/emulation/promises.d.ts +9 -14
  10. package/dist/emulation/promises.js +71 -48
  11. package/dist/emulation/shared.d.ts +22 -0
  12. package/dist/emulation/shared.js +6 -0
  13. package/dist/emulation/sync.d.ts +11 -20
  14. package/dist/emulation/sync.js +44 -23
  15. package/package.json +4 -2
  16. package/scripts/test.js +14 -1
  17. package/src/backends/backend.ts +160 -0
  18. package/src/backends/fetch.ts +180 -0
  19. package/src/backends/file_index.ts +206 -0
  20. package/src/backends/memory.ts +50 -0
  21. package/src/backends/overlay.ts +560 -0
  22. package/src/backends/port/fs.ts +335 -0
  23. package/src/backends/port/readme.md +54 -0
  24. package/src/backends/port/rpc.ts +167 -0
  25. package/src/backends/readme.md +3 -0
  26. package/src/backends/store/fs.ts +700 -0
  27. package/src/backends/store/readme.md +9 -0
  28. package/src/backends/store/simple.ts +146 -0
  29. package/src/backends/store/store.ts +173 -0
  30. package/src/config.ts +173 -0
  31. package/src/credentials.ts +31 -0
  32. package/src/devices.ts +459 -0
  33. package/src/emulation/async.ts +834 -0
  34. package/src/emulation/cache.ts +44 -0
  35. package/src/emulation/constants.ts +182 -0
  36. package/src/emulation/dir.ts +138 -0
  37. package/src/emulation/index.ts +8 -0
  38. package/src/emulation/path.ts +440 -0
  39. package/src/emulation/promises.ts +1133 -0
  40. package/src/emulation/shared.ts +160 -0
  41. package/src/emulation/streams.ts +34 -0
  42. package/src/emulation/sync.ts +867 -0
  43. package/src/emulation/watchers.ts +193 -0
  44. package/src/error.ts +307 -0
  45. package/src/file.ts +661 -0
  46. package/src/filesystem.ts +174 -0
  47. package/src/index.ts +25 -0
  48. package/src/inode.ts +132 -0
  49. package/src/mixins/async.ts +208 -0
  50. package/src/mixins/index.ts +5 -0
  51. package/src/mixins/mutexed.ts +257 -0
  52. package/src/mixins/readonly.ts +96 -0
  53. package/src/mixins/shared.ts +25 -0
  54. package/src/mixins/sync.ts +58 -0
  55. package/src/polyfills.ts +21 -0
  56. package/src/stats.ts +363 -0
  57. package/src/utils.ts +288 -0
  58. package/tests/fs/readdir.test.ts +3 -3
@@ -0,0 +1,206 @@
1
+ /* Note: this file is named file_index.ts because Typescript has special behavior regarding index.ts which can't be disabled. */
2
+
3
+ import { isJSON } from 'utilium';
4
+ import { basename, dirname } from '../emulation/path.js';
5
+ import { Errno, ErrnoError } from '../error.js';
6
+ import { NoSyncFile, isWriteable } from '../file.js';
7
+ import { FileSystem } from '../filesystem.js';
8
+ import { Readonly } from '../mixins/readonly.js';
9
+ import type { StatsLike } from '../stats.js';
10
+ import { Stats } from '../stats.js';
11
+ import { decodeUTF8, encodeUTF8 } from '../utils.js';
12
+
13
+ /**
14
+ * An Index in JSON form
15
+ * @internal
16
+ */
17
+ export interface IndexData {
18
+ version: 1;
19
+ entries: Record<string, StatsLike<number>>;
20
+ }
21
+
22
+ export const version = 1;
23
+
24
+ /**
25
+ * An index of files
26
+ * @internal
27
+ */
28
+ export class Index extends Map<string, Stats> {
29
+ /**
30
+ * Convenience method
31
+ */
32
+ public files(): Map<string, Stats> {
33
+ const files = new Map<string, Stats>();
34
+ for (const [path, stats] of this) {
35
+ if (stats.isFile()) {
36
+ files.set(path, stats);
37
+ }
38
+ }
39
+ return files;
40
+ }
41
+
42
+ /**
43
+ * Converts the index to JSON
44
+ */
45
+ public toJSON(): IndexData {
46
+ return {
47
+ version,
48
+ entries: Object.fromEntries(this),
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Converts the index to a string
54
+ */
55
+ public toString(): string {
56
+ return JSON.stringify(this.toJSON());
57
+ }
58
+
59
+ /**
60
+ * Returns the files in the directory `dir`.
61
+ * This is expensive so it is only called once per directory.
62
+ */
63
+ protected dirEntries(dir: string): string[] {
64
+ const entries = [];
65
+ for (const entry of this.keys()) {
66
+ if (dirname(entry) == dir) {
67
+ entries.push(basename(entry));
68
+ }
69
+ }
70
+ return entries;
71
+ }
72
+
73
+ /**
74
+ * Loads the index from JSON data
75
+ */
76
+ public fromJSON(json: IndexData): void {
77
+ if (json.version != version) {
78
+ throw new ErrnoError(Errno.EINVAL, 'Index version mismatch');
79
+ }
80
+
81
+ this.clear();
82
+
83
+ for (const [path, data] of Object.entries(json.entries)) {
84
+ const stats = new Stats(data);
85
+ if (stats.isDirectory()) {
86
+ stats.fileData = encodeUTF8(JSON.stringify(this.dirEntries(path)));
87
+ }
88
+ this.set(path, stats);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Parses an index from a string
94
+ */
95
+ public static parse(data: string): Index {
96
+ if (!isJSON(data)) {
97
+ throw new ErrnoError(Errno.EINVAL, 'Invalid JSON');
98
+ }
99
+
100
+ const json = JSON.parse(data) as IndexData;
101
+ const index = new Index();
102
+ index.fromJSON(json);
103
+ return index;
104
+ }
105
+ }
106
+
107
+ export abstract class IndexFS extends Readonly(FileSystem) {
108
+ protected index: Index = new Index();
109
+
110
+ protected _isInitialized: boolean = false;
111
+
112
+ public async ready(): Promise<void> {
113
+ await super.ready();
114
+ if (this._isInitialized) {
115
+ return;
116
+ }
117
+ this.index.fromJSON(await this.indexData);
118
+ this._isInitialized = true;
119
+ }
120
+
121
+ public constructor(private indexData: IndexData | Promise<IndexData>) {
122
+ super();
123
+ }
124
+
125
+ public async reloadFiles(): Promise<void> {
126
+ for (const [path, stats] of this.index.files()) {
127
+ delete stats.fileData;
128
+ stats.fileData = await this.getData(path, stats);
129
+ }
130
+ }
131
+
132
+ public reloadFilesSync(): void {
133
+ for (const [path, stats] of this.index.files()) {
134
+ delete stats.fileData;
135
+ stats.fileData = this.getDataSync(path, stats);
136
+ }
137
+ }
138
+
139
+ public stat(path: string): Promise<Stats> {
140
+ return Promise.resolve(this.statSync(path));
141
+ }
142
+
143
+ public statSync(path: string): Stats {
144
+ if (!this.index.has(path)) {
145
+ throw ErrnoError.With('ENOENT', path, 'stat');
146
+ }
147
+
148
+ return this.index.get(path)!;
149
+ }
150
+
151
+ public async openFile(path: string, flag: string): Promise<NoSyncFile<this>> {
152
+ if (isWriteable(flag)) {
153
+ // You can't write to files on this file system.
154
+ throw new ErrnoError(Errno.EPERM, path);
155
+ }
156
+
157
+ // Check if the path exists, and is a file.
158
+ const stats = this.index.get(path);
159
+
160
+ if (!stats) {
161
+ throw ErrnoError.With('ENOENT', path, 'openFile');
162
+ }
163
+
164
+ return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : await this.getData(path, stats));
165
+ }
166
+
167
+ public openFileSync(path: string, flag: string): NoSyncFile<this> {
168
+ if (isWriteable(flag)) {
169
+ // You can't write to files on this file system.
170
+ throw new ErrnoError(Errno.EPERM, path);
171
+ }
172
+
173
+ // Check if the path exists, and is a file.
174
+ const stats = this.index.get(path);
175
+
176
+ if (!stats) {
177
+ throw ErrnoError.With('ENOENT', path, 'openFile');
178
+ }
179
+
180
+ return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.getDataSync(path, stats));
181
+ }
182
+
183
+ public readdir(path: string): Promise<string[]> {
184
+ return Promise.resolve(this.readdirSync(path));
185
+ }
186
+
187
+ public readdirSync(path: string): string[] {
188
+ // Check if it exists.
189
+ const stats = this.index.get(path);
190
+ if (!stats) {
191
+ throw ErrnoError.With('ENOENT', path, 'readdir');
192
+ }
193
+
194
+ const content: unknown = JSON.parse(decodeUTF8(stats.fileData));
195
+ if (!Array.isArray(content)) {
196
+ throw ErrnoError.With('ENODATA', path, 'readdir');
197
+ }
198
+ if (!content.every(item => typeof item == 'string')) {
199
+ throw ErrnoError.With('ENODATA', path, 'readdir');
200
+ }
201
+ return content;
202
+ }
203
+
204
+ protected abstract getData(path: string, stats: Stats): Promise<Uint8Array>;
205
+ protected abstract getDataSync(path: string, stats: Stats): Uint8Array;
206
+ }
@@ -0,0 +1,50 @@
1
+ import type { Ino } from '../inode.js';
2
+ import type { Backend } from './backend.js';
3
+ import { StoreFS } from './store/fs.js';
4
+ import { SimpleTransaction, type SimpleSyncStore } from './store/simple.js';
5
+
6
+ /**
7
+ * A simple in-memory store
8
+ */
9
+ export class InMemoryStore extends Map<Ino, Uint8Array> implements SimpleSyncStore {
10
+ public constructor(public name: string = 'tmp') {
11
+ super();
12
+ }
13
+
14
+ public async sync(): Promise<void> {}
15
+
16
+ public clearSync(): void {
17
+ this.clear();
18
+ }
19
+
20
+ public transaction(): SimpleTransaction {
21
+ return new SimpleTransaction(this);
22
+ }
23
+ }
24
+
25
+ /**
26
+ * A simple in-memory file system backed by an InMemoryStore.
27
+ * Files are not persisted across page loads.
28
+ */
29
+ const _InMemory = {
30
+ name: 'InMemory',
31
+ isAvailable(): boolean {
32
+ return true;
33
+ },
34
+ options: {
35
+ name: {
36
+ type: 'string',
37
+ required: false,
38
+ description: 'The name of the store',
39
+ },
40
+ },
41
+ create({ name }: { name?: string }) {
42
+ const fs = new StoreFS(new InMemoryStore(name));
43
+ fs.checkRootSync();
44
+ return fs;
45
+ },
46
+ } as const satisfies Backend<StoreFS<InMemoryStore>, { name?: string }>;
47
+ type _InMemory = typeof _InMemory;
48
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
49
+ export interface InMemory extends _InMemory {}
50
+ export const InMemory: InMemory = _InMemory;