@zenfs/core 0.9.2 → 0.9.4

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/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
+ }