@zenfs/core 0.9.2 → 0.9.3
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/package.json +2 -9
- package/src/ApiError.ts +310 -0
- package/src/backends/AsyncStore.ts +635 -0
- package/src/backends/InMemory.ts +56 -0
- package/src/backends/Index.ts +500 -0
- package/src/backends/Locked.ts +181 -0
- package/src/backends/Overlay.ts +591 -0
- package/src/backends/SyncStore.ts +589 -0
- package/src/backends/backend.ts +152 -0
- package/src/config.ts +101 -0
- package/src/cred.ts +21 -0
- package/src/emulation/async.ts +910 -0
- package/src/emulation/constants.ts +176 -0
- package/src/emulation/dir.ts +139 -0
- package/src/emulation/index.ts +8 -0
- package/src/emulation/path.ts +468 -0
- package/src/emulation/promises.ts +1071 -0
- package/src/emulation/shared.ts +128 -0
- package/src/emulation/streams.ts +33 -0
- package/src/emulation/sync.ts +898 -0
- package/src/file.ts +721 -0
- package/src/filesystem.ts +546 -0
- package/src/index.ts +21 -0
- package/src/inode.ts +229 -0
- package/src/mutex.ts +52 -0
- package/src/stats.ts +385 -0
- package/src/utils.ts +287 -0
package/src/utils.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import type { OptionalTuple } from 'utilium';
|
|
2
|
+
import { ApiError, ErrorCode } from './ApiError.js';
|
|
3
|
+
import { Cred } from './cred.js';
|
|
4
|
+
import { dirname, resolve } from './emulation/path.js';
|
|
5
|
+
import { FileSystem } from './filesystem.js';
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
function atob(data: string): string;
|
|
9
|
+
function btoa(data: string): string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare const globalThis: {
|
|
13
|
+
setImmediate?: (callback: () => unknown) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Synchronous recursive makedir.
|
|
18
|
+
* @hidden
|
|
19
|
+
*/
|
|
20
|
+
export function mkdirpSync(p: string, mode: number, cred: Cred, fs: FileSystem): void {
|
|
21
|
+
if (!fs.existsSync(p, cred)) {
|
|
22
|
+
mkdirpSync(dirname(p), mode, cred, fs);
|
|
23
|
+
fs.mkdirSync(p, mode, cred);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function _min(d0: number, d1: number, d2: number, bx: number, ay: number): number {
|
|
28
|
+
return Math.min(d0 + 1, d1 + 1, d2 + 1, bx === ay ? d1 : d1 + 1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Calculates levenshtein distance.
|
|
33
|
+
* @hidden
|
|
34
|
+
*/
|
|
35
|
+
export function levenshtein(a: string, b: string): number {
|
|
36
|
+
if (a === b) {
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (a.length > b.length) {
|
|
41
|
+
[a, b] = [b, a]; // Swap a and b
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let la = a.length;
|
|
45
|
+
let lb = b.length;
|
|
46
|
+
|
|
47
|
+
// Trim common suffix
|
|
48
|
+
while (la > 0 && a.charCodeAt(la - 1) === b.charCodeAt(lb - 1)) {
|
|
49
|
+
la--;
|
|
50
|
+
lb--;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let offset = 0;
|
|
54
|
+
|
|
55
|
+
// Trim common prefix
|
|
56
|
+
while (offset < la && a.charCodeAt(offset) === b.charCodeAt(offset)) {
|
|
57
|
+
offset++;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
la -= offset;
|
|
61
|
+
lb -= offset;
|
|
62
|
+
|
|
63
|
+
if (la === 0 || lb === 1) {
|
|
64
|
+
return lb;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const vector = new Array<number>(la << 1);
|
|
68
|
+
|
|
69
|
+
for (let y = 0; y < la; ) {
|
|
70
|
+
vector[la + y] = a.charCodeAt(offset + y);
|
|
71
|
+
vector[y] = ++y;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let x: number;
|
|
75
|
+
let d0: number;
|
|
76
|
+
let d1: number;
|
|
77
|
+
let d2: number;
|
|
78
|
+
let d3: number;
|
|
79
|
+
for (x = 0; x + 3 < lb; ) {
|
|
80
|
+
const bx0 = b.charCodeAt(offset + (d0 = x));
|
|
81
|
+
const bx1 = b.charCodeAt(offset + (d1 = x + 1));
|
|
82
|
+
const bx2 = b.charCodeAt(offset + (d2 = x + 2));
|
|
83
|
+
const bx3 = b.charCodeAt(offset + (d3 = x + 3));
|
|
84
|
+
let dd = (x += 4);
|
|
85
|
+
for (let y = 0; y < la; ) {
|
|
86
|
+
const ay = vector[la + y];
|
|
87
|
+
const dy = vector[y];
|
|
88
|
+
d0 = _min(dy, d0, d1, bx0, ay);
|
|
89
|
+
d1 = _min(d0, d1, d2, bx1, ay);
|
|
90
|
+
d2 = _min(d1, d2, d3, bx2, ay);
|
|
91
|
+
dd = _min(d2, d3, dd, bx3, ay);
|
|
92
|
+
vector[y++] = dd;
|
|
93
|
+
d3 = d2;
|
|
94
|
+
d2 = d1;
|
|
95
|
+
d1 = d0;
|
|
96
|
+
d0 = dy;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let dd: number = 0;
|
|
101
|
+
for (; x < lb; ) {
|
|
102
|
+
const bx0 = b.charCodeAt(offset + (d0 = x));
|
|
103
|
+
dd = ++x;
|
|
104
|
+
for (let y = 0; y < la; y++) {
|
|
105
|
+
const dy = vector[y];
|
|
106
|
+
vector[y] = dd = dy < d0 || dd < d0 ? (dy > dd ? dd + 1 : dy + 1) : bx0 === vector[la + y] ? d0 : d0 + 1;
|
|
107
|
+
d0 = dy;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return dd;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @hidden
|
|
116
|
+
*/
|
|
117
|
+
export const setImmediate = typeof globalThis.setImmediate == 'function' ? globalThis.setImmediate : cb => setTimeout(cb, 0);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Encodes a string into a buffer
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
export function encode(input: string): Uint8Array {
|
|
124
|
+
if (typeof input != 'string') {
|
|
125
|
+
throw new ApiError(ErrorCode.EINVAL, 'Can not encode a non-string');
|
|
126
|
+
}
|
|
127
|
+
return new Uint8Array(Array.from(input).map(char => char.charCodeAt(0)));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Decodes a string from a buffer
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
export function decode(input?: Uint8Array): string {
|
|
135
|
+
if (!(input instanceof Uint8Array)) {
|
|
136
|
+
throw new ApiError(ErrorCode.EINVAL, 'Can not decode a non-Uint8Array');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return Array.from(input)
|
|
140
|
+
.map(char => String.fromCharCode(char))
|
|
141
|
+
.join('');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Decodes a directory listing
|
|
146
|
+
* @hidden
|
|
147
|
+
*/
|
|
148
|
+
export function decodeDirListing(data: Uint8Array): Record<string, bigint> {
|
|
149
|
+
return JSON.parse(decode(data), (k, v) => {
|
|
150
|
+
if (k == '') {
|
|
151
|
+
return v;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return BigInt(v);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Encodes a directory listing
|
|
160
|
+
* @hidden
|
|
161
|
+
*/
|
|
162
|
+
export function encodeDirListing(data: Record<string, bigint>): Uint8Array {
|
|
163
|
+
return encode(
|
|
164
|
+
JSON.stringify(data, (k, v) => {
|
|
165
|
+
if (k == '') {
|
|
166
|
+
return v;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return v.toString();
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export type Callback<Args extends unknown[] = []> = (e?: ApiError, ...args: OptionalTuple<Args>) => unknown;
|
|
175
|
+
|
|
176
|
+
import type { EncodingOption, OpenMode, WriteFileOptions } from 'node:fs';
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* converts Date or number to a integer UNIX timestamp
|
|
180
|
+
* Grabbed from NodeJS sources (lib/fs.js)
|
|
181
|
+
*
|
|
182
|
+
* @internal
|
|
183
|
+
*/
|
|
184
|
+
export function _toUnixTimestamp(time: Date | number): number {
|
|
185
|
+
if (typeof time === 'number') {
|
|
186
|
+
return Math.floor(time);
|
|
187
|
+
}
|
|
188
|
+
if (time instanceof Date) {
|
|
189
|
+
return Math.floor(time.getTime() / 1000);
|
|
190
|
+
}
|
|
191
|
+
throw new Error('Cannot parse time: ' + time);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Normalizes a mode
|
|
196
|
+
* @internal
|
|
197
|
+
*/
|
|
198
|
+
export function normalizeMode(mode: string | number | unknown, def?: number): number {
|
|
199
|
+
if (typeof mode == 'number') {
|
|
200
|
+
return mode;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (typeof mode == 'string') {
|
|
204
|
+
const parsed = parseInt(mode, 8);
|
|
205
|
+
if (!isNaN(parsed)) {
|
|
206
|
+
return parsed;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (typeof def == 'number') {
|
|
211
|
+
return def;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid mode: ' + mode?.toString());
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Normalizes a time
|
|
219
|
+
* @internal
|
|
220
|
+
*/
|
|
221
|
+
export function normalizeTime(time: string | number | Date): Date {
|
|
222
|
+
if (time instanceof Date) {
|
|
223
|
+
return time;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (typeof time == 'number') {
|
|
227
|
+
return new Date(time * 1000);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (typeof time == 'string') {
|
|
231
|
+
return new Date(time);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid time.');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Normalizes a path
|
|
239
|
+
* @internal
|
|
240
|
+
*/
|
|
241
|
+
export function normalizePath(p: string): string {
|
|
242
|
+
// Node doesn't allow null characters in paths.
|
|
243
|
+
if (p.includes('\x00')) {
|
|
244
|
+
throw new ApiError(ErrorCode.EINVAL, 'Path must be a string without null bytes.');
|
|
245
|
+
}
|
|
246
|
+
if (p.length == 0) {
|
|
247
|
+
throw new ApiError(ErrorCode.EINVAL, 'Path must not be empty.');
|
|
248
|
+
}
|
|
249
|
+
return resolve(p.replaceAll(/[/\\]+/g, '/'));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Normalizes options
|
|
254
|
+
* @param options options to normalize
|
|
255
|
+
* @param encoding default encoding
|
|
256
|
+
* @param flag default flag
|
|
257
|
+
* @param mode default mode
|
|
258
|
+
* @internal
|
|
259
|
+
*/
|
|
260
|
+
export function normalizeOptions(
|
|
261
|
+
options?: WriteFileOptions | (EncodingOption & { flag?: OpenMode }),
|
|
262
|
+
encoding: BufferEncoding = 'utf8',
|
|
263
|
+
flag?: string,
|
|
264
|
+
mode: number = 0
|
|
265
|
+
): { encoding: BufferEncoding; flag: string; mode: number } {
|
|
266
|
+
if (typeof options != 'object' || options === null) {
|
|
267
|
+
return {
|
|
268
|
+
encoding: typeof options == 'string' ? options : encoding,
|
|
269
|
+
flag,
|
|
270
|
+
mode,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
encoding: typeof options?.encoding == 'string' ? options.encoding : encoding,
|
|
276
|
+
flag: typeof options?.flag == 'string' ? options.flag : flag,
|
|
277
|
+
mode: normalizeMode('mode' in options ? options?.mode : null, mode),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Do nothing
|
|
283
|
+
* @internal
|
|
284
|
+
*/
|
|
285
|
+
export function nop() {
|
|
286
|
+
// do nothing
|
|
287
|
+
}
|