@legit-sdk/core 0.4.4 → 0.5.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.
- package/dist/index.d.ts +700 -192
- package/dist/index.js +179 -112
- package/dist/server.d.ts +6 -2
- package/dist/server.js +16 -32
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { fileSave, FileWithHandle } from 'browser-fs-access';
|
|
|
2
2
|
import * as nodeFs from 'node:fs';
|
|
3
3
|
import { MakeDirectoryOptions, Mode } from 'node:fs';
|
|
4
4
|
import * as memfs_lib_node_types_misc_js from 'memfs/lib/node/types/misc.js';
|
|
5
|
-
import { IDir, TFileHandleReadResult, TData, TMode, IStats, TTime, TFileHandleWriteResult, TFileHandleWritevResult, TFileHandleReadvResult, IFileHandle, TDataOut, IDirent
|
|
5
|
+
import { IDir, PathLike, TFileHandleReadResult, TData, TMode, IStats, TTime, TFileHandleWriteResult, TFileHandleWritevResult, TFileHandleReadvResult, IFileHandle, TDataOut, IDirent } from 'memfs/lib/node/types/misc.js';
|
|
6
6
|
import { IAppendFileOptions, IStatOptions, IReadFileOptions, IWriteFileOptions, IReadableWebStreamOptions } from 'memfs/lib/node/types/options.js';
|
|
7
7
|
import { PathLike as PathLike$1 } from 'fs';
|
|
8
8
|
import { IFs } from 'memfs';
|
|
@@ -28,6 +28,117 @@ type LegitAuth = {
|
|
|
28
28
|
|
|
29
29
|
type CompositeSubFsDir = Pick<IDir, "path" | "close" | "read" | typeof Symbol.asyncIterator>;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Context information for filesystem operations
|
|
33
|
+
* This context is provided by CompositeFs when routing to a specific SubFS
|
|
34
|
+
*/
|
|
35
|
+
interface FsOperationContext {
|
|
36
|
+
/** The full original path that was requested */
|
|
37
|
+
fullPath: string;
|
|
38
|
+
/** Extracted route parameters (e.g., { branchName: 'main', filePath: 'path/to/file' }) */
|
|
39
|
+
params: Record<string, string>;
|
|
40
|
+
/** Static siblings from route matching */
|
|
41
|
+
staticSiblings: {
|
|
42
|
+
segment: string;
|
|
43
|
+
type: 'folder' | 'file';
|
|
44
|
+
}[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare abstract class BaseCompositeSubFs implements CompositeSubFs {
|
|
48
|
+
/** Reference to the parent CompositeFs instance - late init */
|
|
49
|
+
compositeFs: CompositeFs;
|
|
50
|
+
/** Context for the current filesystem operation */
|
|
51
|
+
context?: FsOperationContext;
|
|
52
|
+
newContext?: FsOperationContext;
|
|
53
|
+
fsType: FsType;
|
|
54
|
+
/**
|
|
55
|
+
* Unique instance ID that persists across contextual instances
|
|
56
|
+
* This allows us to check if two contextual instances wrap the same base SubFS
|
|
57
|
+
*/
|
|
58
|
+
readonly rootInstanceId: string;
|
|
59
|
+
name: string;
|
|
60
|
+
rootPath: string;
|
|
61
|
+
constructor({ name, rootPath }: {
|
|
62
|
+
name: string;
|
|
63
|
+
rootPath: string;
|
|
64
|
+
});
|
|
65
|
+
attach(compositFs: CompositeFs): void;
|
|
66
|
+
/**
|
|
67
|
+
* Create a new instance with context bound to it
|
|
68
|
+
* This creates a shallow copy where all mutable state (like open file handles)
|
|
69
|
+
* is shared between the original and the contextual instance, but the context
|
|
70
|
+
* is unique to this instance.
|
|
71
|
+
*
|
|
72
|
+
* Each operation gets its own contextual instance, ensuring that concurrent
|
|
73
|
+
* operations don't interfere with each other's context.
|
|
74
|
+
*/
|
|
75
|
+
withContext(context: FsOperationContext): this;
|
|
76
|
+
abstract responsible(filePath: string): Promise<boolean>;
|
|
77
|
+
abstract fileType(): number;
|
|
78
|
+
open(path: PathLike, flags: string, mode?: number): Promise<CompositFsFileHandle>;
|
|
79
|
+
access(path: PathLike, mode?: number): Promise<void>;
|
|
80
|
+
stat(path: PathLike, opts?: {
|
|
81
|
+
bigint?: false;
|
|
82
|
+
}): Promise<nodeFs.Stats>;
|
|
83
|
+
stat(path: PathLike, opts: {
|
|
84
|
+
bigint: true;
|
|
85
|
+
}): Promise<nodeFs.BigIntStats>;
|
|
86
|
+
stat(path: PathLike, opts?: {
|
|
87
|
+
bigint?: boolean;
|
|
88
|
+
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
89
|
+
lstat(path: PathLike, opts?: {
|
|
90
|
+
bigint?: false;
|
|
91
|
+
}): Promise<nodeFs.Stats>;
|
|
92
|
+
lstat(path: PathLike, opts: {
|
|
93
|
+
bigint: true;
|
|
94
|
+
}): Promise<nodeFs.BigIntStats>;
|
|
95
|
+
lstat(path: PathLike, opts?: {
|
|
96
|
+
bigint?: boolean;
|
|
97
|
+
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
98
|
+
opendir(path: PathLike, options?: nodeFs.OpenDirOptions): Promise<CompositeSubFsDir>;
|
|
99
|
+
link(existingPath: PathLike, newPath: PathLike): Promise<void>;
|
|
100
|
+
mkdir(path: PathLike, options?: nodeFs.MakeDirectoryOptions | nodeFs.Mode | null): Promise<void>;
|
|
101
|
+
readDirFiltering?(path: PathLike, entries: IDir[] | string[]): Promise<string[]>;
|
|
102
|
+
readdir(path: PathLike, options?: (nodeFs.ObjectEncodingOptions & {
|
|
103
|
+
withFileTypes?: false | undefined;
|
|
104
|
+
recursive?: boolean | undefined;
|
|
105
|
+
}) | BufferEncoding | null): Promise<string[]>;
|
|
106
|
+
readdir(path: PathLike, options?: {
|
|
107
|
+
encoding: 'buffer';
|
|
108
|
+
withFileTypes?: false | undefined;
|
|
109
|
+
recursive?: boolean | undefined;
|
|
110
|
+
} | 'buffer' | null): Promise<Buffer[]>;
|
|
111
|
+
readdir(path: PathLike, options?: (nodeFs.ObjectEncodingOptions & {
|
|
112
|
+
withFileTypes?: false | undefined;
|
|
113
|
+
recursive?: boolean | undefined;
|
|
114
|
+
}) | BufferEncoding | null): Promise<string[] | Buffer[]>;
|
|
115
|
+
readdir(path: PathLike, options: nodeFs.ObjectEncodingOptions & {
|
|
116
|
+
withFileTypes: true;
|
|
117
|
+
recursive?: boolean | undefined;
|
|
118
|
+
}): Promise<nodeFs.Dirent[]>;
|
|
119
|
+
readlink(path: PathLike, ...args: any[]): Promise<any>;
|
|
120
|
+
unlink(path: PathLike): Promise<void>;
|
|
121
|
+
rename(oldPath: PathLike, newPath: PathLike): Promise<void>;
|
|
122
|
+
rmdir(path: PathLike, ...args: any[]): Promise<void>;
|
|
123
|
+
symlink(target: PathLike, path: PathLike, type?: string | null): Promise<void>;
|
|
124
|
+
lookup(filePath: string): Promise<number>;
|
|
125
|
+
resolvePath(fd: number): string;
|
|
126
|
+
close(fh: CompositFsFileHandle): Promise<void>;
|
|
127
|
+
dataSync(fh: CompositFsFileHandle): Promise<void>;
|
|
128
|
+
read(fh: CompositFsFileHandle, buffer: Buffer | Uint8Array, offset: number, length: number, position: number): Promise<TFileHandleReadResult>;
|
|
129
|
+
appendFile(fh: CompositFsFileHandle, data: TData, options?: IAppendFileOptions | string): Promise<void>;
|
|
130
|
+
fchmod(fh: CompositFsFileHandle, mode: TMode): Promise<void>;
|
|
131
|
+
fchown(fh: CompositFsFileHandle, uid: number, gid: number): Promise<void>;
|
|
132
|
+
ftruncate(fh: CompositFsFileHandle, len?: number): Promise<void>;
|
|
133
|
+
fstat(fh: CompositFsFileHandle, options?: IStatOptions): Promise<IStats>;
|
|
134
|
+
futimes(fh: CompositFsFileHandle, atime: TTime, mtime: TTime): Promise<void>;
|
|
135
|
+
write(fh: CompositFsFileHandle, buffer: Buffer | ArrayBufferView | DataView, offset?: number, length?: number, position?: number): Promise<TFileHandleWriteResult>;
|
|
136
|
+
writev(fh: CompositFsFileHandle, buffers: ArrayBufferView[], position?: number | null): Promise<TFileHandleWritevResult>;
|
|
137
|
+
readv(fh: CompositFsFileHandle, buffers: ArrayBufferView[], position?: number | null): Promise<TFileHandleReadvResult>;
|
|
138
|
+
readFile(path: PathLike | IFileHandle, options?: IReadFileOptions | string): Promise<TDataOut>;
|
|
139
|
+
writeFile(path: string, data: TData, options: IWriteFileOptions | string): Promise<void>;
|
|
140
|
+
}
|
|
141
|
+
|
|
31
142
|
type FileHandleDelegate = {
|
|
32
143
|
name: string;
|
|
33
144
|
fileType: () => number;
|
|
@@ -49,9 +160,24 @@ type FileHandleDelegate = {
|
|
|
49
160
|
readv(fh: CompositFsFileHandle, buffers: ArrayBufferView[], position?: number | null): Promise<TFileHandleReadvResult>;
|
|
50
161
|
};
|
|
51
162
|
|
|
163
|
+
type FsType = 'folder' | 'file' | 'fs';
|
|
52
164
|
type CompositeSubFs = Pick<typeof nodeFs.promises, 'access' | 'link' | 'readdir' | 'readlink' | 'unlink' | 'rename' | 'rmdir' | 'symlink'> & {
|
|
53
165
|
name: string;
|
|
54
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Explicit declaration of the filesystem type.
|
|
168
|
+
* - 'folder': This SubFS only handles directories
|
|
169
|
+
* - 'file': This SubFS only handles files
|
|
170
|
+
* - 'fs': This SubFS handles both files and folders (default)
|
|
171
|
+
*/
|
|
172
|
+
fsType: FsType;
|
|
173
|
+
context?: FsOperationContext;
|
|
174
|
+
newContext?: FsOperationContext;
|
|
175
|
+
readonly rootInstanceId: string;
|
|
176
|
+
compositeFs: CompositeFs;
|
|
177
|
+
withContext(context: FsOperationContext): BaseCompositeSubFs;
|
|
178
|
+
attach(compositFs: CompositeFs): void;
|
|
179
|
+
readDirFiltering?(path: nodeFs.PathLike, entries: string[]): Promise<string[]>;
|
|
180
|
+
readFile(path: nodeFs.PathLike, options?: IReadFileOptions | string): Promise<TDataOut>;
|
|
55
181
|
stat(path: nodeFs.PathLike, opts?: IStatOptions & {
|
|
56
182
|
bigint?: false;
|
|
57
183
|
}): Promise<IStats<number>>;
|
|
@@ -162,17 +288,138 @@ declare const createFsOperationFileLogger: (fs: {
|
|
|
162
288
|
operationArgs: any;
|
|
163
289
|
}) => Promise<void>;
|
|
164
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Routes filesystem paths to appropriate SubFS handlers using a pattern-based system.
|
|
293
|
+
*
|
|
294
|
+
* The PathRouter supports a flexible pattern syntax for defining dynamic routes:
|
|
295
|
+
*
|
|
296
|
+
* | Pattern | Description | Example | Matches |
|
|
297
|
+
* |----------------|-----------------------|-------------------|------------------------------------------------------|
|
|
298
|
+
* | `static` | Static segment | `branches` | `branches` only |
|
|
299
|
+
* | `[param]` | Dynamic segment | `[branchName]` | `main`, `dev`, etc. (single segment) |
|
|
300
|
+
* | `[[...param]]` | Optional catch-all | `[[...filePath]]` | matches zero or more segments |
|
|
301
|
+
* | `.` | Folder index handler | `{ '.': handler }`| handles the folder itself |
|
|
302
|
+
*
|
|
303
|
+
* Priority is given to more specific routes (static > dynamic > catch-all).
|
|
304
|
+
*
|
|
305
|
+
* ## Static Sibling Union
|
|
306
|
+
*
|
|
307
|
+
* When multiple route patterns match at the same parent path level, their static siblings
|
|
308
|
+
* are merged (union operation). For example, if both `.legit` and `[[...filePath]]/.legit`
|
|
309
|
+
* match at root, the static siblings from both routes are combined.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* const router = new PathRouter({
|
|
314
|
+
* '[[...filePath]]': {
|
|
315
|
+
* '.': catchAllHandler,
|
|
316
|
+
* '.legit': { changes: changesHandler }
|
|
317
|
+
* },
|
|
318
|
+
* '.legit': {
|
|
319
|
+
* '.': legitHandler,
|
|
320
|
+
* head: headHandler,
|
|
321
|
+
* branches: { ... }
|
|
322
|
+
* }
|
|
323
|
+
* }, '/root');
|
|
324
|
+
*
|
|
325
|
+
* // Match with highest priority handler and union of all static siblings
|
|
326
|
+
* const result = router.match('/root/.legit');
|
|
327
|
+
* // result.handler === legitHandler (higher priority)
|
|
328
|
+
* // result.staticSiblings === [
|
|
329
|
+
* // { segment: 'head', type: 'file' }, // from .legit route
|
|
330
|
+
* // { segment: 'branches', type: 'folder' }, // from .legit route
|
|
331
|
+
* // { segment: 'changes', type: 'file' } // from [[...filePath]]/.legit
|
|
332
|
+
* // ]
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
335
|
+
|
|
336
|
+
type MatchResult = {
|
|
337
|
+
handler: CompositeSubFs;
|
|
338
|
+
matchingPattern: string;
|
|
339
|
+
staticSiblings: {
|
|
340
|
+
segment: string;
|
|
341
|
+
type: 'folder' | 'file';
|
|
342
|
+
}[];
|
|
343
|
+
params: Record<string, string>;
|
|
344
|
+
};
|
|
345
|
+
interface LegitRouteFolder {
|
|
346
|
+
[key: string]: PathRouteDescription;
|
|
347
|
+
}
|
|
348
|
+
type PathRouteDescription = CompositeSubFs | LegitRouteFolder;
|
|
349
|
+
/**
|
|
350
|
+
* PathRouter matches filesystem paths to SubFS handlers using pattern-based routing.
|
|
351
|
+
*
|
|
352
|
+
* The router evaluates all compiled patterns twice:
|
|
353
|
+
* 1. To find the highest-priority matching handler for the requested path
|
|
354
|
+
* 2. To find all patterns that match the parent path, merging their static siblings (union)
|
|
355
|
+
*
|
|
356
|
+
* This allows dynamic routes like `[[...filePath]]/.legit` to contribute siblings
|
|
357
|
+
* that are visible when matching the static `.legit` route.
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* const router = new PathRouter({
|
|
362
|
+
* '[[...filePath]]': {
|
|
363
|
+
* '.': catchAllHandler,
|
|
364
|
+
* '.legit': { changes: changesHandler }
|
|
365
|
+
* },
|
|
366
|
+
* '.legit': {
|
|
367
|
+
* '.': legitHandler,
|
|
368
|
+
* head: headHandler,
|
|
369
|
+
* }
|
|
370
|
+
* }, '/root');
|
|
371
|
+
*
|
|
372
|
+
* const result = router.match('/root/.legit');
|
|
373
|
+
* // Returns:
|
|
374
|
+
* // {
|
|
375
|
+
* // handler: legitHandler, // highest priority match
|
|
376
|
+
* // params: {},
|
|
377
|
+
* // staticSiblings: [
|
|
378
|
+
* // { segment: 'head', type: 'file' },
|
|
379
|
+
* // { segment: 'changes', type: 'file' } // merged from catchall route!
|
|
380
|
+
* // ]
|
|
381
|
+
* // }
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
384
|
+
declare class PathRouter {
|
|
385
|
+
routes: LegitRouteFolder;
|
|
386
|
+
private rootPath;
|
|
387
|
+
private compiledRoutes;
|
|
388
|
+
/**
|
|
389
|
+
* Creates a new PathRouter with the given route configuration.
|
|
390
|
+
*
|
|
391
|
+
* @param routes - The route tree defining all possible paths
|
|
392
|
+
* @param rootPath - The root path to strip from incoming paths (e.g., '/root')
|
|
393
|
+
*/
|
|
394
|
+
constructor(routes: LegitRouteFolder, rootPath: string);
|
|
395
|
+
/**
|
|
396
|
+
* Matches a path against all route patterns and returns the best match.
|
|
397
|
+
*
|
|
398
|
+
* The matching algorithm:
|
|
399
|
+
* 1. Normalizes the path by removing the root prefix
|
|
400
|
+
* 2. Finds all route patterns that match the path (by priority order)
|
|
401
|
+
* 3. Returns the highest-priority match
|
|
402
|
+
* 4. For static siblings, evaluates ALL patterns against the parent path
|
|
403
|
+
* and merges their siblings (union operation)
|
|
404
|
+
*
|
|
405
|
+
* @param path - The full filesystem path to match
|
|
406
|
+
* @returns Match result with handler, params, and union of static siblings, or undefined if no match
|
|
407
|
+
*/
|
|
408
|
+
match(path: string): MatchResult | undefined;
|
|
409
|
+
}
|
|
410
|
+
|
|
165
411
|
/**
|
|
166
412
|
*
|
|
167
413
|
* The CompositFs handles distribution of file operations to its sub filesystems and keeps track of open file handles.
|
|
168
|
-
*
|
|
414
|
+
*
|
|
415
|
+
* open() returns a CompositFsFileHandle that wraps the real filehandle from the responsible SubFs and allows
|
|
169
416
|
* to forward operations
|
|
170
417
|
*
|
|
171
418
|
* Each SubFs determines if it is responsible for a given path. The Composite fs probes each subsystem for responsibility.
|
|
172
419
|
*
|
|
173
420
|
* The responisbility is probed in the following order:
|
|
174
421
|
*
|
|
175
|
-
* hiddenFilesFileSystem -> ephemeralFilesFileSystem -> other subFs in order of addition
|
|
422
|
+
* hiddenFilesFileSystem -> ephemeralFilesFileSystem -> other subFs in order of addition
|
|
176
423
|
*
|
|
177
424
|
* Composit fs consists of two special sub filesystems:
|
|
178
425
|
*
|
|
@@ -223,52 +470,42 @@ declare class CompositeFs {
|
|
|
223
470
|
writeFile: (file: nodeFs.PathOrFileDescriptor, data: string | Buffer | Uint8Array, options?: any) => Promise<void>;
|
|
224
471
|
getFilehandle: (fd: number) => CompositFsFileHandle | undefined;
|
|
225
472
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
hiddenFilesFileSystem: CompositeSubFs | undefined;
|
|
229
|
-
passThroughFileSystem: CompositeSubFs;
|
|
230
|
-
subFilesystems: CompositeSubFs[];
|
|
473
|
+
filterLayers: CompositeSubFs[];
|
|
474
|
+
router: PathRouter;
|
|
231
475
|
parentFs: CompositeFs | undefined;
|
|
232
476
|
name: string;
|
|
233
|
-
|
|
234
|
-
gitCache: any;
|
|
477
|
+
rootPath: string;
|
|
235
478
|
pathToFileDescriptors: Map<
|
|
236
479
|
/** path */
|
|
237
480
|
string, number[]>;
|
|
238
481
|
openFileHandles: Map<number, CompositFsFileHandle>;
|
|
239
482
|
logOperation: FsOperationLogger | undefined;
|
|
240
483
|
private getNextFileDescriptor;
|
|
241
|
-
constructor({ name,
|
|
484
|
+
constructor({ name, filterLayers, routes, rootPath, }: {
|
|
242
485
|
name: string;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
486
|
+
filterLayers: CompositeSubFs[];
|
|
487
|
+
routes: LegitRouteFolder;
|
|
488
|
+
rootPath: string;
|
|
246
489
|
});
|
|
247
490
|
setLoggger(logger: FsOperationLogger | undefined): void;
|
|
248
491
|
logOperationOnFileDescsriptor(fd: CompositFsFileHandle, operation: string, args: any): Promise<void>;
|
|
249
492
|
getFilehandle(fd: number): CompositFsFileHandle | undefined;
|
|
250
|
-
setEphemeralFilesSubFs(subFs: CompositeSubFs): void;
|
|
251
|
-
setHiddenFilesSubFs(subFs: CompositeSubFs): void;
|
|
252
|
-
addSubFs(subFs: CompositeSubFs): void;
|
|
253
493
|
/**
|
|
254
|
-
*
|
|
494
|
+
* Helper function that takes a filePath and returns the sub fs that is responsible for it.
|
|
495
|
+
*
|
|
496
|
+
* Order is:
|
|
497
|
+
* hidden -> if hidden no more questions - hide it!
|
|
498
|
+
*
|
|
499
|
+
* ephemeral -> file is marked as ephemeral *.DS_STORE and lock files - ignore if versioned at some point - always handle it as ephemeral
|
|
500
|
+
* versioned ->
|
|
501
|
+
* nonVersioned -> files
|
|
502
|
+
*
|
|
503
|
+
* other subFs in order of addition -> passThrough
|
|
255
504
|
* @param filePath
|
|
256
|
-
* @returns
|
|
505
|
+
* @returns A contextual SubFS instance with route information bound to it
|
|
257
506
|
*/
|
|
258
507
|
private getResponsibleFs;
|
|
259
508
|
access(filePath: string, mode?: number): Promise<void>;
|
|
260
|
-
opendir(dirPath: nodeFs.PathLike, options?: nodeFs.OpenDirOptions): Promise<CompositeFsDir>;
|
|
261
|
-
mkdir(dirPath: string, options?: any): Promise<void>;
|
|
262
|
-
/**
|
|
263
|
-
* Read dir needs to check if one subfs takes control.
|
|
264
|
-
*
|
|
265
|
-
* @param dirPath
|
|
266
|
-
* @param options
|
|
267
|
-
* @returns
|
|
268
|
-
*/
|
|
269
|
-
readdir(dirPath: nodeFs.PathLike, options?: any): Promise<string[]>;
|
|
270
|
-
open(filePath: string, flags: string, mode?: number): Promise<CompositFsFileHandle>;
|
|
271
|
-
close(fh: CompositFsFileHandle): Promise<void>;
|
|
272
509
|
stat(path: nodeFs.PathLike, opts?: {
|
|
273
510
|
bigint?: false;
|
|
274
511
|
}): Promise<nodeFs.Stats>;
|
|
@@ -287,11 +524,23 @@ declare class CompositeFs {
|
|
|
287
524
|
lstat(path: nodeFs.PathLike, opts?: {
|
|
288
525
|
bigint?: boolean;
|
|
289
526
|
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
527
|
+
unlink(filePath: nodeFs.PathLike): Promise<void>;
|
|
528
|
+
mkdir(dirPath: string, options?: any): Promise<void>;
|
|
529
|
+
rmdir(dirPath: nodeFs.PathLike, options?: nodeFs.RmDirOptions): Promise<void>;
|
|
530
|
+
opendir(dirPath: nodeFs.PathLike, options?: nodeFs.OpenDirOptions): Promise<CompositeFsDir>;
|
|
531
|
+
/**
|
|
532
|
+
* Read dir needs to check if one subfs takes control.
|
|
533
|
+
*
|
|
534
|
+
* @param dirPath
|
|
535
|
+
* @param options
|
|
536
|
+
* @returns
|
|
537
|
+
*/
|
|
538
|
+
readdir(dirPath: nodeFs.PathLike, options?: any): Promise<string[]>;
|
|
539
|
+
open(filePath: string, flags: string, mode?: number): Promise<CompositFsFileHandle>;
|
|
540
|
+
close(fh: CompositFsFileHandle): Promise<void>;
|
|
290
541
|
link(existingPath: nodeFs.PathLike, newPath: nodeFs.PathLike): Promise<void>;
|
|
291
542
|
readlink(path: nodeFs.PathLike, options?: nodeFs.ObjectEncodingOptions | BufferEncoding | null): Promise<void>;
|
|
292
|
-
unlink(filePath: nodeFs.PathLike): Promise<void>;
|
|
293
543
|
rename(oldPath: nodeFs.PathLike, newPath: nodeFs.PathLike): Promise<void>;
|
|
294
|
-
rmdir(dirPath: nodeFs.PathLike, options?: nodeFs.RmDirOptions): Promise<void>;
|
|
295
544
|
symlink(target: nodeFs.PathLike, path: nodeFs.PathLike, type?: string | null): Promise<void>;
|
|
296
545
|
readFile(path: nodeFs.PathOrFileDescriptor, options?: {
|
|
297
546
|
encoding?: null;
|
|
@@ -308,6 +557,7 @@ declare class CompositeFs {
|
|
|
308
557
|
} | BufferEncoding | null): Promise<void>;
|
|
309
558
|
}
|
|
310
559
|
|
|
560
|
+
declare function mergeLegitRouteFolders(a: LegitRouteFolder, b: LegitRouteFolder): LegitRouteFolder;
|
|
311
561
|
declare function openLegitFsWithMemoryFs(props?: Parameters<typeof openLegitFs>[0]): Promise<CompositeFs & {
|
|
312
562
|
auth: LegitAuth;
|
|
313
563
|
sync: {
|
|
@@ -317,16 +567,35 @@ declare function openLegitFsWithMemoryFs(props?: Parameters<typeof openLegitFs>[
|
|
|
317
567
|
loadBranch: (branch: string) => Promise<void>;
|
|
318
568
|
sequentialPush: (branchesToPush: string[]) => Promise<void>;
|
|
319
569
|
};
|
|
570
|
+
_storageFs: CompositeFs;
|
|
320
571
|
setLogger(logger: FsOperationLogger | undefined): void;
|
|
321
572
|
push: (branches: string[]) => Promise<void>;
|
|
322
573
|
shareCurrentBranch: () => Promise<string>;
|
|
323
574
|
setCurrentBranch: (branch: string) => Promise<void>;
|
|
324
575
|
getCurrentBranch: () => Promise<string>;
|
|
576
|
+
/**
|
|
577
|
+
*
|
|
578
|
+
* This function takes a legit archive - earlier compressed with saveArchive and writes it to storage fs.
|
|
579
|
+
*
|
|
580
|
+
* Refs that can be fast forwarded should get updeted - referecences that cannot be fast forwarded create a ref named branchname-conflict-uuid.
|
|
581
|
+
* New Refs should be added. (TODO how do we handle deleted refs to prevent them from coming back?)
|
|
582
|
+
*
|
|
583
|
+
* The git config should get ignored for now
|
|
584
|
+
*
|
|
585
|
+
* @param legitArchieve a zlib compressed legit repo
|
|
586
|
+
*/
|
|
587
|
+
loadArchive: (legitArchieve: Uint8Array) => Promise<void>;
|
|
588
|
+
/**
|
|
589
|
+
* creates a legit archieve - a compressed representation of the legit repo (the .git folder in the storage fs)
|
|
590
|
+
*
|
|
591
|
+
* @returns
|
|
592
|
+
*/
|
|
593
|
+
saveArchive: () => Promise<Uint8Array>;
|
|
325
594
|
}>;
|
|
326
595
|
/**
|
|
327
596
|
* Creates and configures a LegitFs instance with CompositeFs, GitSubFs, HiddenFileSubFs, and EphemeralSubFs.
|
|
328
597
|
*/
|
|
329
|
-
declare function openLegitFs({ storageFs, gitRoot, anonymousBranch, showKeepFiles, initialAuthor, serverUrl, publicKey,
|
|
598
|
+
declare function openLegitFs({ storageFs, gitRoot, anonymousBranch, showKeepFiles, initialAuthor, serverUrl, publicKey, ephemaralGitConfig, additionalFilterLayers, routeOverrides, }: {
|
|
330
599
|
storageFs: typeof nodeFs;
|
|
331
600
|
gitRoot: string;
|
|
332
601
|
anonymousBranch?: string;
|
|
@@ -334,7 +603,9 @@ declare function openLegitFs({ storageFs, gitRoot, anonymousBranch, showKeepFile
|
|
|
334
603
|
initialAuthor?: LegitUser;
|
|
335
604
|
serverUrl?: string;
|
|
336
605
|
publicKey?: string;
|
|
337
|
-
|
|
606
|
+
ephemaralGitConfig?: boolean;
|
|
607
|
+
additionalFilterLayers?: CompositeSubFs[];
|
|
608
|
+
routeOverrides?: LegitRouteFolder;
|
|
338
609
|
}): Promise<CompositeFs & {
|
|
339
610
|
auth: LegitAuth;
|
|
340
611
|
sync: {
|
|
@@ -344,11 +615,30 @@ declare function openLegitFs({ storageFs, gitRoot, anonymousBranch, showKeepFile
|
|
|
344
615
|
loadBranch: (branch: string) => Promise<void>;
|
|
345
616
|
sequentialPush: (branchesToPush: string[]) => Promise<void>;
|
|
346
617
|
};
|
|
618
|
+
_storageFs: CompositeFs;
|
|
347
619
|
setLogger(logger: FsOperationLogger | undefined): void;
|
|
348
620
|
push: (branches: string[]) => Promise<void>;
|
|
349
621
|
shareCurrentBranch: () => Promise<string>;
|
|
350
622
|
setCurrentBranch: (branch: string) => Promise<void>;
|
|
351
623
|
getCurrentBranch: () => Promise<string>;
|
|
624
|
+
/**
|
|
625
|
+
*
|
|
626
|
+
* This function takes a legit archive - earlier compressed with saveArchive and writes it to storage fs.
|
|
627
|
+
*
|
|
628
|
+
* Refs that can be fast forwarded should get updeted - referecences that cannot be fast forwarded create a ref named branchname-conflict-uuid.
|
|
629
|
+
* New Refs should be added. (TODO how do we handle deleted refs to prevent them from coming back?)
|
|
630
|
+
*
|
|
631
|
+
* The git config should get ignored for now
|
|
632
|
+
*
|
|
633
|
+
* @param legitArchieve a zlib compressed legit repo
|
|
634
|
+
*/
|
|
635
|
+
loadArchive: (legitArchieve: Uint8Array) => Promise<void>;
|
|
636
|
+
/**
|
|
637
|
+
* creates a legit archieve - a compressed representation of the legit repo (the .git folder in the storage fs)
|
|
638
|
+
*
|
|
639
|
+
* @returns
|
|
640
|
+
*/
|
|
641
|
+
saveArchive: () => Promise<Uint8Array>;
|
|
352
642
|
}>;
|
|
353
643
|
|
|
354
644
|
type FileAccess = {
|
|
@@ -358,81 +648,6 @@ declare function getLegitFsAccess(legitFs: Awaited<ReturnType<typeof openLegitFs
|
|
|
358
648
|
openFile: (filePath: string) => Promise<FileWithHandle>;
|
|
359
649
|
}>;
|
|
360
650
|
|
|
361
|
-
declare abstract class BaseCompositeSubFs implements CompositeSubFs {
|
|
362
|
-
protected toStr(p: any): string;
|
|
363
|
-
protected compositFs: CompositeFs;
|
|
364
|
-
protected gitRoot: string;
|
|
365
|
-
name: string;
|
|
366
|
-
constructor({ name, parentFs, gitRoot, }: {
|
|
367
|
-
name: string;
|
|
368
|
-
parentFs: CompositeFs;
|
|
369
|
-
gitRoot: string;
|
|
370
|
-
});
|
|
371
|
-
abstract responsible(filePath: string): Promise<boolean>;
|
|
372
|
-
abstract fileType(): number;
|
|
373
|
-
open(path: PathLike, flags: string, mode?: number): Promise<CompositFsFileHandle>;
|
|
374
|
-
access(path: PathLike, mode?: number): Promise<void>;
|
|
375
|
-
stat(path: PathLike, opts?: {
|
|
376
|
-
bigint?: false;
|
|
377
|
-
}): Promise<nodeFs.Stats>;
|
|
378
|
-
stat(path: PathLike, opts: {
|
|
379
|
-
bigint: true;
|
|
380
|
-
}): Promise<nodeFs.BigIntStats>;
|
|
381
|
-
stat(path: PathLike, opts?: {
|
|
382
|
-
bigint?: boolean;
|
|
383
|
-
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
384
|
-
lstat(path: PathLike, opts?: {
|
|
385
|
-
bigint?: false;
|
|
386
|
-
}): Promise<nodeFs.Stats>;
|
|
387
|
-
lstat(path: PathLike, opts: {
|
|
388
|
-
bigint: true;
|
|
389
|
-
}): Promise<nodeFs.BigIntStats>;
|
|
390
|
-
lstat(path: PathLike, opts?: {
|
|
391
|
-
bigint?: boolean;
|
|
392
|
-
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
393
|
-
opendir(path: PathLike, options?: nodeFs.OpenDirOptions): Promise<CompositeSubFsDir>;
|
|
394
|
-
link(existingPath: PathLike, newPath: PathLike): Promise<void>;
|
|
395
|
-
mkdir(path: PathLike, options?: nodeFs.MakeDirectoryOptions | nodeFs.Mode | null): Promise<void>;
|
|
396
|
-
readdir(path: PathLike, options?: (nodeFs.ObjectEncodingOptions & {
|
|
397
|
-
withFileTypes?: false | undefined;
|
|
398
|
-
recursive?: boolean | undefined;
|
|
399
|
-
}) | BufferEncoding | null): Promise<string[]>;
|
|
400
|
-
readdir(path: PathLike, options?: {
|
|
401
|
-
encoding: 'buffer';
|
|
402
|
-
withFileTypes?: false | undefined;
|
|
403
|
-
recursive?: boolean | undefined;
|
|
404
|
-
} | 'buffer' | null): Promise<Buffer[]>;
|
|
405
|
-
readdir(path: PathLike, options?: (nodeFs.ObjectEncodingOptions & {
|
|
406
|
-
withFileTypes?: false | undefined;
|
|
407
|
-
recursive?: boolean | undefined;
|
|
408
|
-
}) | BufferEncoding | null): Promise<string[] | Buffer[]>;
|
|
409
|
-
readdir(path: PathLike, options: nodeFs.ObjectEncodingOptions & {
|
|
410
|
-
withFileTypes: true;
|
|
411
|
-
recursive?: boolean | undefined;
|
|
412
|
-
}): Promise<nodeFs.Dirent[]>;
|
|
413
|
-
readlink(path: PathLike, ...args: any[]): Promise<any>;
|
|
414
|
-
unlink(path: PathLike): Promise<void>;
|
|
415
|
-
rename(oldPath: PathLike, newPath: PathLike): Promise<void>;
|
|
416
|
-
rmdir(path: PathLike, ...args: any[]): Promise<void>;
|
|
417
|
-
symlink(target: PathLike, path: PathLike, type?: string | null): Promise<void>;
|
|
418
|
-
lookup(filePath: string): Promise<number>;
|
|
419
|
-
resolvePath(fd: number): string;
|
|
420
|
-
close(fh: CompositFsFileHandle): Promise<void>;
|
|
421
|
-
dataSync(fh: CompositFsFileHandle): Promise<void>;
|
|
422
|
-
read(fh: CompositFsFileHandle, buffer: Buffer | Uint8Array, offset: number, length: number, position: number): Promise<TFileHandleReadResult>;
|
|
423
|
-
appendFile(fh: CompositFsFileHandle, data: TData, options?: IAppendFileOptions | string): Promise<void>;
|
|
424
|
-
fchmod(fh: CompositFsFileHandle, mode: TMode): Promise<void>;
|
|
425
|
-
fchown(fh: CompositFsFileHandle, uid: number, gid: number): Promise<void>;
|
|
426
|
-
ftruncate(fh: CompositFsFileHandle, len?: number): Promise<void>;
|
|
427
|
-
fstat(fh: CompositFsFileHandle, options?: IStatOptions): Promise<IStats>;
|
|
428
|
-
futimes(fh: CompositFsFileHandle, atime: TTime, mtime: TTime): Promise<void>;
|
|
429
|
-
write(fh: CompositFsFileHandle, buffer: Buffer | ArrayBufferView | DataView, offset?: number, length?: number, position?: number): Promise<TFileHandleWriteResult>;
|
|
430
|
-
writev(fh: CompositFsFileHandle, buffers: ArrayBufferView[], position?: number | null): Promise<TFileHandleWritevResult>;
|
|
431
|
-
readv(fh: CompositFsFileHandle, buffers: ArrayBufferView[], position?: number | null): Promise<TFileHandleReadvResult>;
|
|
432
|
-
readFile(path: PathLike | IFileHandle, options?: IReadFileOptions | string): Promise<TDataOut>;
|
|
433
|
-
writeFile(path: string, data: TData, options: IWriteFileOptions | string): Promise<void>;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
651
|
/**
|
|
437
652
|
* FS utilized to hide files, it is responsible for files found in hiddenFiles
|
|
438
653
|
*
|
|
@@ -440,11 +655,10 @@ declare abstract class BaseCompositeSubFs implements CompositeSubFs {
|
|
|
440
655
|
*/
|
|
441
656
|
declare class HiddenFileSubFs extends BaseCompositeSubFs {
|
|
442
657
|
private ig;
|
|
443
|
-
constructor({ name,
|
|
658
|
+
constructor({ name, hiddenFiles, rootPath, }: {
|
|
444
659
|
name: string;
|
|
445
|
-
parentFs: CompositeFs;
|
|
446
|
-
gitRoot: string;
|
|
447
660
|
hiddenFiles: string[];
|
|
661
|
+
rootPath: string;
|
|
448
662
|
});
|
|
449
663
|
responsible(filePath: string): Promise<boolean>;
|
|
450
664
|
fileType(): number;
|
|
@@ -456,6 +670,7 @@ declare class HiddenFileSubFs extends BaseCompositeSubFs {
|
|
|
456
670
|
opendir(path: PathLike$1): Promise<any>;
|
|
457
671
|
link(existingPath: PathLike$1): Promise<void>;
|
|
458
672
|
mkdir(path: PathLike$1): Promise<any>;
|
|
673
|
+
readDirFiltering?(path: PathLike$1, entries: string[]): Promise<string[]>;
|
|
459
674
|
readdir(path: PathLike$1): Promise<any>;
|
|
460
675
|
readlink(path: PathLike$1): Promise<any>;
|
|
461
676
|
unlink(path: PathLike$1): Promise<void>;
|
|
@@ -479,10 +694,9 @@ declare class EphemeralSubFs extends BaseCompositeSubFs {
|
|
|
479
694
|
private ig;
|
|
480
695
|
patterns: string[];
|
|
481
696
|
private normalizePath;
|
|
482
|
-
constructor({ name,
|
|
697
|
+
constructor({ name, rootPath, ephemeralPatterns, }: {
|
|
483
698
|
name: string;
|
|
484
|
-
|
|
485
|
-
gitRoot: string;
|
|
699
|
+
rootPath: string;
|
|
486
700
|
ephemeralPatterns: string[];
|
|
487
701
|
});
|
|
488
702
|
responsible(filePath: string): Promise<boolean>;
|
|
@@ -534,49 +748,72 @@ declare class EphemeralSubFs extends BaseCompositeSubFs {
|
|
|
534
748
|
}
|
|
535
749
|
|
|
536
750
|
/**
|
|
537
|
-
*
|
|
751
|
+
* Copy-on-Write filesystem implementation
|
|
752
|
+
*
|
|
753
|
+
* This SubFs provides copy-on-write semantics:
|
|
754
|
+
* - Reads check copyToFs first, then fall back to sourceFs
|
|
755
|
+
* - Writes always go to copyToFs (creating a copy if needed)
|
|
756
|
+
* - Configured with patterns to determine which files to track
|
|
757
|
+
* - Original files in sourceFs are never modified
|
|
538
758
|
*/
|
|
539
|
-
declare class
|
|
759
|
+
declare class CopyOnWriteSubFs extends BaseCompositeSubFs {
|
|
540
760
|
private openFh;
|
|
541
|
-
private
|
|
542
|
-
private
|
|
543
|
-
|
|
761
|
+
private sourceFs;
|
|
762
|
+
private copyToFs;
|
|
763
|
+
private copyPath;
|
|
764
|
+
private ig;
|
|
765
|
+
patterns: string[];
|
|
766
|
+
constructor({ name, sourceFs, copyToFs, copyToRootPath, rootPath, patterns, }: {
|
|
544
767
|
name: string;
|
|
545
|
-
|
|
546
|
-
|
|
768
|
+
sourceFs: any;
|
|
769
|
+
copyToFs: any;
|
|
770
|
+
copyToRootPath: string;
|
|
771
|
+
rootPath: string;
|
|
772
|
+
patterns: string[];
|
|
547
773
|
});
|
|
774
|
+
private normalizeCopyPath;
|
|
775
|
+
private normalizePath;
|
|
548
776
|
responsible(filePath: string): Promise<boolean>;
|
|
549
777
|
fileType(): number;
|
|
778
|
+
/**
|
|
779
|
+
* Check if a file has been copied (exists in copyToFs)
|
|
780
|
+
*/
|
|
781
|
+
private isCopied;
|
|
782
|
+
/**
|
|
783
|
+
* Copy a file from sourceFs to copyToFs
|
|
784
|
+
*/
|
|
785
|
+
private copyFromSource;
|
|
550
786
|
open(filePath: string, flags: string, mode?: number): Promise<CompositFsFileHandle>;
|
|
551
787
|
access(filePath: PathLike$1, mode?: number): Promise<void>;
|
|
552
|
-
stat(
|
|
788
|
+
stat(statPath: PathLike$1, opts?: {
|
|
553
789
|
bigint?: false;
|
|
554
790
|
}): Promise<nodeFs.Stats>;
|
|
555
|
-
stat(
|
|
791
|
+
stat(statPath: PathLike$1, opts: {
|
|
556
792
|
bigint: true;
|
|
557
793
|
}): Promise<nodeFs.BigIntStats>;
|
|
558
|
-
stat(
|
|
794
|
+
stat(statPath: PathLike$1, opts?: {
|
|
559
795
|
bigint?: boolean;
|
|
560
796
|
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
561
|
-
lstat(
|
|
797
|
+
lstat(lstatPath: PathLike$1, opts?: {
|
|
562
798
|
bigint?: false;
|
|
563
799
|
}): Promise<nodeFs.Stats>;
|
|
564
|
-
lstat(
|
|
800
|
+
lstat(lstatPath: PathLike$1, opts: {
|
|
565
801
|
bigint: true;
|
|
566
802
|
}): Promise<nodeFs.BigIntStats>;
|
|
567
|
-
lstat(
|
|
803
|
+
lstat(lstatPath: PathLike$1, opts?: {
|
|
568
804
|
bigint?: boolean;
|
|
569
805
|
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
570
|
-
opendir(folderPath: nodeFs.PathLike, options?: nodeFs.OpenDirOptions): Promise<
|
|
806
|
+
opendir(folderPath: nodeFs.PathLike, options?: nodeFs.OpenDirOptions): Promise<CompositeSubFsDir>;
|
|
571
807
|
link(existingPath: PathLike$1, newPath: PathLike$1): Promise<void>;
|
|
572
808
|
mkdir(dirPath: PathLike$1, options?: nodeFs.MakeDirectoryOptions | nodeFs.Mode | null): Promise<void>;
|
|
573
|
-
readdir(
|
|
574
|
-
readlink(
|
|
575
|
-
unlink(
|
|
809
|
+
readdir(readdirPath: PathLike$1, ...args: any[]): Promise<any>;
|
|
810
|
+
readlink(readlinkPath: PathLike$1): Promise<any>;
|
|
811
|
+
unlink(unlinkPath: PathLike$1): Promise<void>;
|
|
576
812
|
rename(oldPath: PathLike$1, newPath: PathLike$1): Promise<void>;
|
|
577
|
-
rmdir(
|
|
578
|
-
symlink(target: PathLike$1,
|
|
813
|
+
rmdir(rmdirPath: PathLike$1, options?: nodeFs.RmDirOptions): Promise<void>;
|
|
814
|
+
symlink(target: PathLike$1, linkPath: PathLike$1, type?: string | null): Promise<void>;
|
|
579
815
|
lookup(filePath: string): Promise<number>;
|
|
816
|
+
resolvePath(fd: number): string;
|
|
580
817
|
close(fh: CompositFsFileHandle): Promise<void>;
|
|
581
818
|
dataSync(fh: CompositFsFileHandle): Promise<void>;
|
|
582
819
|
read(fh: CompositFsFileHandle, buffer: Buffer | Uint8Array, offset: number, length: number, position: number): Promise<TFileHandleReadResult>;
|
|
@@ -584,13 +821,12 @@ declare class PassThroughSubFs extends BaseCompositeSubFs {
|
|
|
584
821
|
fchown(fh: CompositFsFileHandle, uid: number, gid: number): Promise<void>;
|
|
585
822
|
write(fh: CompositFsFileHandle, buffer: Buffer | ArrayBufferView | DataView, offset?: number, length?: number, position?: number): Promise<TFileHandleWriteResult>;
|
|
586
823
|
ftruncate(fh: CompositFsFileHandle, len?: number): Promise<void>;
|
|
587
|
-
resolvePath(fd: number): string;
|
|
588
824
|
fstat(fh: CompositFsFileHandle, options?: IStatOptions): Promise<IStats>;
|
|
589
825
|
futimes(fh: CompositFsFileHandle, atime: TTime, mtime: TTime): Promise<void>;
|
|
590
826
|
writev(fh: CompositFsFileHandle, buffers: ArrayBufferView[], position?: number | null): Promise<TFileHandleWritevResult>;
|
|
591
827
|
readv(fh: CompositFsFileHandle, buffers: ArrayBufferView[], position?: number | null): Promise<TFileHandleReadvResult>;
|
|
592
|
-
readFile(
|
|
593
|
-
writeFile(
|
|
828
|
+
readFile(filePath: PathLike$1 | IFileHandle, options?: IReadFileOptions | string): Promise<TDataOut>;
|
|
829
|
+
writeFile(filePath: string, data: TData, options: IWriteFileOptions | string): Promise<void>;
|
|
594
830
|
}
|
|
595
831
|
|
|
596
832
|
type VirtualFile = {
|
|
@@ -608,9 +844,7 @@ type VirtualFile = {
|
|
|
608
844
|
interface VirtualFileArgs {
|
|
609
845
|
cacheFs: IFs;
|
|
610
846
|
filePath: string;
|
|
611
|
-
gitRoot: string;
|
|
612
847
|
userSpaceFs: CompositeFs;
|
|
613
|
-
nodeFs?: any;
|
|
614
848
|
pathParams: any;
|
|
615
849
|
author: {
|
|
616
850
|
name: string;
|
|
@@ -619,6 +853,14 @@ interface VirtualFileArgs {
|
|
|
619
853
|
timezoneOffset: number;
|
|
620
854
|
};
|
|
621
855
|
}
|
|
856
|
+
/**
|
|
857
|
+
* Definition of a virtual file in the Git subsystem
|
|
858
|
+
*
|
|
859
|
+
* ->
|
|
860
|
+
*
|
|
861
|
+
* writeFile -> writes the whole file
|
|
862
|
+
* readFile -> writes the whole file
|
|
863
|
+
*/
|
|
622
864
|
type VirtualFileDefinition = {
|
|
623
865
|
type: string;
|
|
624
866
|
rootType: 'folder' | 'file';
|
|
@@ -639,73 +881,52 @@ type VirtualFileDefinition = {
|
|
|
639
881
|
rmdir?: (args: VirtualFileArgs) => Promise<void>;
|
|
640
882
|
};
|
|
641
883
|
|
|
642
|
-
interface LegitRouteFolder {
|
|
643
|
-
[key: string]: LegitRouteDescriptor;
|
|
644
|
-
}
|
|
645
|
-
type LegitRouteDescriptor = VirtualFileDefinition | LegitRouteFolder;
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Topics to discuss:
|
|
649
|
-
* # Ref space polution
|
|
650
|
-
* - Local vs Remote (we dont push all refs all the time)
|
|
651
|
-
* - required refs (what are the miniumum refs required to keep the repo healthy)
|
|
652
|
-
* - ref branch concept (branch pointing to oids to keep refs alive withouth poluting refs)
|
|
653
|
-
*
|
|
654
|
-
* # Performance (we tackle whens appear)
|
|
655
|
-
* - Git operations (especially history traversal) can be expensive
|
|
656
|
-
* - Current memfs caching might not scale for large repos
|
|
657
|
-
* - Consider lazy loading and bounded caches
|
|
658
|
-
*/
|
|
659
884
|
/**
|
|
660
|
-
*
|
|
885
|
+
* CompositeSubFsAdapter - Adapts a virtual file handler to work as a SubFS
|
|
661
886
|
*
|
|
887
|
+
* This class adapts a single VirtualFileDefinition to work as a CompositeSubFs.
|
|
888
|
+
* It receives routing context (extracted parameters) from CompositeFs, eliminating the need for internal routing.
|
|
662
889
|
*
|
|
663
|
-
*
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
* - we chunk the file and sstsore chunks as blobs in git
|
|
667
|
-
**/
|
|
668
|
-
declare class GitSubFs extends BaseCompositeSubFs implements CompositeSubFs {
|
|
669
|
-
private static readonly LEGIT_DIR;
|
|
670
|
-
private pathRouter;
|
|
890
|
+
* Each virtual file type (branch file, commit file, etc.) gets its own adapter instance.
|
|
891
|
+
*/
|
|
892
|
+
declare class CompositeSubFsAdapter extends BaseCompositeSubFs implements CompositeSubFs {
|
|
671
893
|
private memFs;
|
|
672
894
|
private openFh;
|
|
673
895
|
storageFs: any;
|
|
896
|
+
protected gitRoot: string;
|
|
897
|
+
/**
|
|
898
|
+
* The virtual file handler for this adapter
|
|
899
|
+
* This defines how to read/write/stat the virtual file
|
|
900
|
+
*/
|
|
901
|
+
handler: VirtualFileDefinition;
|
|
674
902
|
getAuthor(): Promise<{
|
|
675
903
|
name: string;
|
|
676
904
|
email: string;
|
|
677
905
|
date: number;
|
|
678
906
|
timezoneOffset: number;
|
|
679
907
|
}>;
|
|
680
|
-
constructor({ name,
|
|
908
|
+
constructor({ name, gitStorageFs, gitRoot, handler, rootPath, }: {
|
|
681
909
|
name: string;
|
|
682
|
-
parentFs: CompositeFs;
|
|
683
910
|
gitStorageFs: any;
|
|
684
911
|
gitRoot: string;
|
|
685
|
-
|
|
912
|
+
handler: VirtualFileDefinition;
|
|
913
|
+
rootPath: string;
|
|
686
914
|
});
|
|
687
915
|
responsible(filePath: string): Promise<boolean>;
|
|
688
|
-
private getRouteHandler;
|
|
689
916
|
/**
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
*
|
|
696
|
-
*
|
|
917
|
+
* Get route parameters from the operation context
|
|
918
|
+
* CompositeFs sets this context when routing to this adapter
|
|
919
|
+
*/
|
|
920
|
+
private getRouteParams;
|
|
921
|
+
/**
|
|
922
|
+
* Get static siblings from the operation context
|
|
923
|
+
* These are static entries that should appear in directory listings
|
|
924
|
+
*/
|
|
925
|
+
private getStaticSiblings;
|
|
926
|
+
/**
|
|
927
|
+
* Opens a virtual file using the configured handler.
|
|
697
928
|
*
|
|
698
|
-
*
|
|
699
|
-
* @param flags - The file system flags indicating the desired open mode (e.g., "r" for read, "w" for write, "a" for append, "x" for exclusive creation).
|
|
700
|
-
* - "r": Open file for reading. An exception occurs if the file does not exist.
|
|
701
|
-
* - "w": Open file for writing. The file is created (if it does not exist) or truncated (if it exists).
|
|
702
|
-
* - "a": Open file for appending. The file is created if it does not exist.
|
|
703
|
-
* - "x": Exclusive flag. Used with "w" or "a" to fail if the file exists.
|
|
704
|
-
* - Combinations like "wx", "ax", etc., are also supported.
|
|
705
|
-
* @param mode - Optional file mode (permission and sticky bits) to use if creating a file.
|
|
706
|
-
* @returns A promise that resolves to a `CompositFsFileHandle` for the opened file.
|
|
707
|
-
* @throws If the file is not a virtual legit file, if write operations are not allowed for the file type,
|
|
708
|
-
* or if the file does not exist.
|
|
929
|
+
* The handler receives route parameters via context set by CompositeFs.
|
|
709
930
|
*/
|
|
710
931
|
open(filePath: string, flags: string, mode?: number): Promise<CompositFsFileHandle>;
|
|
711
932
|
mkdir(path: PathLike, options?: nodeFs.MakeDirectoryOptions | nodeFs.Mode | null): Promise<void>;
|
|
@@ -776,13 +997,57 @@ declare class GitSubFs extends BaseCompositeSubFs implements CompositeSubFs {
|
|
|
776
997
|
fileType(): number;
|
|
777
998
|
}
|
|
778
999
|
|
|
1000
|
+
/**
|
|
1001
|
+
* Creates a CompositeSubFsAdapter for legit virtual folder operations
|
|
1002
|
+
*
|
|
1003
|
+
* This adapter handles the .legit folder, which serves as the root
|
|
1004
|
+
* for all legit-related virtual files and operations.
|
|
1005
|
+
*
|
|
1006
|
+
* @example
|
|
1007
|
+
* ```ts
|
|
1008
|
+
* const adapter = createLegitVirtualFileAdapter({
|
|
1009
|
+
* gitStorageFs: memFs,
|
|
1010
|
+
* gitRoot: '/my-repo',
|
|
1011
|
+
* });
|
|
1012
|
+
*
|
|
1013
|
+
* // Use in CompositeFs routes
|
|
1014
|
+
* const compositeFs = new CompositeFs({
|
|
1015
|
+
* routes: {
|
|
1016
|
+
* '.legit': adapter,
|
|
1017
|
+
* },
|
|
1018
|
+
* });
|
|
1019
|
+
* ```
|
|
1020
|
+
*/
|
|
1021
|
+
declare function createLegitVirtualFileAdapter({ gitStorageFs, gitRoot, rootPath, }: {
|
|
1022
|
+
gitStorageFs: any;
|
|
1023
|
+
gitRoot: string;
|
|
1024
|
+
rootPath?: string;
|
|
1025
|
+
}): CompositeSubFsAdapter;
|
|
1026
|
+
|
|
779
1027
|
type Operation = {
|
|
780
1028
|
oid: string;
|
|
781
1029
|
parentOids: string[];
|
|
782
1030
|
message: string;
|
|
783
1031
|
originBranchOid?: string;
|
|
784
1032
|
};
|
|
785
|
-
|
|
1033
|
+
/**
|
|
1034
|
+
* Creates a CompositeSubFsAdapter for branch operations history
|
|
1035
|
+
*
|
|
1036
|
+
* This adapter handles reading the history of branch operations.
|
|
1037
|
+
*
|
|
1038
|
+
* @example
|
|
1039
|
+
* ```ts
|
|
1040
|
+
* const adapter = createBranchOperationsAdapter({
|
|
1041
|
+
* gitStorageFs: memFs,
|
|
1042
|
+
* gitRoot: '/my-repo',
|
|
1043
|
+
* });
|
|
1044
|
+
* ```
|
|
1045
|
+
*/
|
|
1046
|
+
declare function createBranchOperationsAdapter({ gitStorageFs, gitRoot, rootPath, }: {
|
|
1047
|
+
gitStorageFs: any;
|
|
1048
|
+
gitRoot: string;
|
|
1049
|
+
rootPath?: string;
|
|
1050
|
+
}): CompositeSubFsAdapter;
|
|
786
1051
|
|
|
787
1052
|
declare const createLegitSyncService: ({ fs, gitRepoPath, serverUrl, auth, anonymousBranch, authHeaderPrefix, }: {
|
|
788
1053
|
fs: FsClient;
|
|
@@ -812,5 +1077,248 @@ type HistoryItem = {
|
|
|
812
1077
|
author: User;
|
|
813
1078
|
};
|
|
814
1079
|
|
|
815
|
-
|
|
816
|
-
|
|
1080
|
+
declare abstract class ASimpleCompositeSubfs extends BaseCompositeSubFs implements CompositeSubFs {
|
|
1081
|
+
private memFs;
|
|
1082
|
+
private openFh;
|
|
1083
|
+
constructor({ name, rootPath }: {
|
|
1084
|
+
name: string;
|
|
1085
|
+
rootPath: string;
|
|
1086
|
+
});
|
|
1087
|
+
/**
|
|
1088
|
+
* Check if write operations are supported by this adapter
|
|
1089
|
+
* Subclasses can override this to return false for read-only adapters
|
|
1090
|
+
*/
|
|
1091
|
+
isWriteSupported(): boolean;
|
|
1092
|
+
responsible(_filePath: string): Promise<boolean>;
|
|
1093
|
+
/**
|
|
1094
|
+
* Get route parameters from the operation context
|
|
1095
|
+
* CompositeFs sets this context when routing to this adapter
|
|
1096
|
+
*/
|
|
1097
|
+
private getRouteParams;
|
|
1098
|
+
/**
|
|
1099
|
+
* Get static siblings from the operation context
|
|
1100
|
+
* These are static entries that should appear in directory listings
|
|
1101
|
+
*/
|
|
1102
|
+
private getStaticSiblings;
|
|
1103
|
+
/**
|
|
1104
|
+
* Opens a virtual file using the configured handler.
|
|
1105
|
+
*
|
|
1106
|
+
* The handler receives route parameters via context set by CompositeFs.
|
|
1107
|
+
*/
|
|
1108
|
+
open(filePath: string, flags: string, mode?: number): Promise<CompositFsFileHandle>;
|
|
1109
|
+
abstract createDirectory(args: {
|
|
1110
|
+
path: string;
|
|
1111
|
+
recursive?: boolean;
|
|
1112
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1113
|
+
}): Promise<void>;
|
|
1114
|
+
abstract readFileContent(args: {
|
|
1115
|
+
path: string;
|
|
1116
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1117
|
+
}): Promise<{
|
|
1118
|
+
content: string | Buffer;
|
|
1119
|
+
oid?: string;
|
|
1120
|
+
} | undefined>;
|
|
1121
|
+
abstract writeFileContent(args: {
|
|
1122
|
+
path: string;
|
|
1123
|
+
content: Buffer | string;
|
|
1124
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1125
|
+
}): Promise<void>;
|
|
1126
|
+
abstract readDirectory(args: {
|
|
1127
|
+
path: string;
|
|
1128
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1129
|
+
}): Promise<nodeFs.Dirent[]>;
|
|
1130
|
+
abstract renamePath(args: {
|
|
1131
|
+
oldPath: string;
|
|
1132
|
+
newPath: string;
|
|
1133
|
+
oldContext: ASimpleCompositeSubfs['context'];
|
|
1134
|
+
newContext: ASimpleCompositeSubfs['context'];
|
|
1135
|
+
}): Promise<void>;
|
|
1136
|
+
abstract deleteFile(args: {
|
|
1137
|
+
path: string;
|
|
1138
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1139
|
+
}): Promise<void>;
|
|
1140
|
+
abstract removeDirectory(args: {
|
|
1141
|
+
path: string;
|
|
1142
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1143
|
+
}): Promise<void>;
|
|
1144
|
+
mkdir(path: PathLike, options?: nodeFs.MakeDirectoryOptions | nodeFs.Mode | null): Promise<void>;
|
|
1145
|
+
access(path: PathLike, _mode?: number): Promise<void>;
|
|
1146
|
+
futimes(fh: CompositFsFileHandle, atime: TTime, mtime: TTime): Promise<void>;
|
|
1147
|
+
fstat(fh: CompositFsFileHandle, options?: IStatOptions): Promise<IStats>;
|
|
1148
|
+
ftruncate(fh: CompositFsFileHandle, len?: number): Promise<void>;
|
|
1149
|
+
stat(path: PathLike, opts?: {
|
|
1150
|
+
bigint?: false;
|
|
1151
|
+
}): Promise<nodeFs.Stats>;
|
|
1152
|
+
stat(path: PathLike, opts: {
|
|
1153
|
+
bigint: true;
|
|
1154
|
+
}): Promise<nodeFs.BigIntStats>;
|
|
1155
|
+
stat(path: PathLike, opts?: {
|
|
1156
|
+
bigint?: boolean;
|
|
1157
|
+
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
1158
|
+
abstract getStats(args: {
|
|
1159
|
+
path: string;
|
|
1160
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1161
|
+
}): Promise<nodeFs.Stats>;
|
|
1162
|
+
lstat(path: PathLike, opts?: {
|
|
1163
|
+
bigint?: false;
|
|
1164
|
+
}): Promise<nodeFs.Stats>;
|
|
1165
|
+
lstat(path: PathLike, opts: {
|
|
1166
|
+
bigint: true;
|
|
1167
|
+
}): Promise<nodeFs.BigIntStats>;
|
|
1168
|
+
lstat(path: PathLike, opts?: {
|
|
1169
|
+
bigint?: boolean;
|
|
1170
|
+
}): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
|
|
1171
|
+
readdir(path: PathLike, options?: (nodeFs.ObjectEncodingOptions & {
|
|
1172
|
+
withFileTypes?: false | undefined;
|
|
1173
|
+
recursive?: boolean | undefined;
|
|
1174
|
+
}) | BufferEncoding | null): Promise<string[]>;
|
|
1175
|
+
readdir(path: PathLike, options?: {
|
|
1176
|
+
encoding: 'buffer';
|
|
1177
|
+
withFileTypes?: false | undefined;
|
|
1178
|
+
recursive?: boolean | undefined;
|
|
1179
|
+
} | 'buffer' | null): Promise<Buffer[]>;
|
|
1180
|
+
readdir(path: PathLike, options?: (nodeFs.ObjectEncodingOptions & {
|
|
1181
|
+
withFileTypes?: false | undefined;
|
|
1182
|
+
recursive?: boolean | undefined;
|
|
1183
|
+
}) | BufferEncoding | null): Promise<string[] | Buffer[]>;
|
|
1184
|
+
readdir(path: PathLike, options: nodeFs.ObjectEncodingOptions & {
|
|
1185
|
+
withFileTypes: true;
|
|
1186
|
+
recursive?: boolean | undefined;
|
|
1187
|
+
}): Promise<nodeFs.Dirent[]>;
|
|
1188
|
+
read(fh: CompositFsFileHandle, buffer: Buffer | Uint8Array, offset: number, length: number, position: number): Promise<TFileHandleReadResult>;
|
|
1189
|
+
/**
|
|
1190
|
+
*
|
|
1191
|
+
* Writes (parts) of a buffer to a specific position in the file
|
|
1192
|
+
*
|
|
1193
|
+
* - a write leads to a new commit and on flush since the point in time a flush may occur may vary a read operation may
|
|
1194
|
+
* not see changed done on the read lays.
|
|
1195
|
+
*
|
|
1196
|
+
*
|
|
1197
|
+
* @param fh
|
|
1198
|
+
* @param buffer
|
|
1199
|
+
* @param offset
|
|
1200
|
+
* @param length
|
|
1201
|
+
* @param position
|
|
1202
|
+
* @returns
|
|
1203
|
+
*/
|
|
1204
|
+
write(fh: CompositFsFileHandle, buffer: Buffer | ArrayBufferView | DataView, offset?: number, length?: number, position?: number): Promise<TFileHandleWriteResult>;
|
|
1205
|
+
close(fh: CompositFsFileHandle): Promise<void>;
|
|
1206
|
+
dataSync(fh: CompositFsFileHandle): Promise<void>;
|
|
1207
|
+
readFile(path: PathLike | IFileHandle, options?: IReadFileOptions | string): Promise<TDataOut>;
|
|
1208
|
+
writeFile(path: string, data: TData, options?: IWriteFileOptions | string): Promise<void>;
|
|
1209
|
+
rename(oldPath: PathLike, newPath: PathLike): Promise<void>;
|
|
1210
|
+
fchmod(_fh: CompositFsFileHandle, _mode: TMode): Promise<void>;
|
|
1211
|
+
unlink(path: PathLike): Promise<void>;
|
|
1212
|
+
rmdir(path: PathLike, ..._args: any[]): Promise<void>;
|
|
1213
|
+
fileType(): number;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
interface MemoryFile {
|
|
1217
|
+
type: 'file';
|
|
1218
|
+
content: Buffer;
|
|
1219
|
+
mode: number;
|
|
1220
|
+
createdAt: Date;
|
|
1221
|
+
modifiedAt: Date;
|
|
1222
|
+
}
|
|
1223
|
+
interface MemoryDirectory {
|
|
1224
|
+
type: 'directory';
|
|
1225
|
+
entries: Set<string>;
|
|
1226
|
+
mode: number;
|
|
1227
|
+
createdAt: Date;
|
|
1228
|
+
modifiedAt: Date;
|
|
1229
|
+
}
|
|
1230
|
+
type MemoryNode = MemoryFile | MemoryDirectory;
|
|
1231
|
+
type FileSystemData = string | {
|
|
1232
|
+
[key: string]: FileSystemData;
|
|
1233
|
+
};
|
|
1234
|
+
/**
|
|
1235
|
+
* SimpleMemorySubFs - A simple in-memory filesystem implementation
|
|
1236
|
+
*
|
|
1237
|
+
* This class extends ASimpleCompositeSubfs and stores all files and directories
|
|
1238
|
+
* in a JavaScript Map in memory. It's useful for testing, caching, or temporary
|
|
1239
|
+
* file storage that doesn't need to persist.
|
|
1240
|
+
*/
|
|
1241
|
+
declare class SimpleMemorySubFs extends ASimpleCompositeSubfs {
|
|
1242
|
+
private storage;
|
|
1243
|
+
private nextFileType;
|
|
1244
|
+
/**
|
|
1245
|
+
* Debug method to inspect internal storage
|
|
1246
|
+
*/
|
|
1247
|
+
_debugGetStorage(): Map<string, MemoryNode>;
|
|
1248
|
+
constructor({ name, rootPath, initialData, }: {
|
|
1249
|
+
name: string;
|
|
1250
|
+
rootPath: string;
|
|
1251
|
+
initialData?: FileSystemData;
|
|
1252
|
+
});
|
|
1253
|
+
/**
|
|
1254
|
+
* Populate the filesystem from initial data structure
|
|
1255
|
+
* @param data - The data structure to populate from (string = file, object = folder)
|
|
1256
|
+
* @param currentPath - The current path in the filesystem
|
|
1257
|
+
*/
|
|
1258
|
+
private populateFromInitialData;
|
|
1259
|
+
fileType(): number;
|
|
1260
|
+
createDirectory(args: {
|
|
1261
|
+
path: string;
|
|
1262
|
+
recursive?: boolean;
|
|
1263
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1264
|
+
}): Promise<void>;
|
|
1265
|
+
getStats(args: {
|
|
1266
|
+
path: string;
|
|
1267
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1268
|
+
}): Promise<nodeFs.Stats>;
|
|
1269
|
+
readFileContent(args: {
|
|
1270
|
+
path: string;
|
|
1271
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1272
|
+
}): Promise<{
|
|
1273
|
+
content: string | Buffer;
|
|
1274
|
+
oid?: string;
|
|
1275
|
+
} | undefined>;
|
|
1276
|
+
writeFileContent(args: {
|
|
1277
|
+
path: string;
|
|
1278
|
+
content: Buffer | string;
|
|
1279
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1280
|
+
}): Promise<void>;
|
|
1281
|
+
readDirectory(args: {
|
|
1282
|
+
path: string;
|
|
1283
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1284
|
+
}): Promise<nodeFs.Dirent[]>;
|
|
1285
|
+
renamePath(args: {
|
|
1286
|
+
oldPath: string;
|
|
1287
|
+
newPath: string;
|
|
1288
|
+
oldContext: ASimpleCompositeSubfs['context'];
|
|
1289
|
+
newContext: ASimpleCompositeSubfs['context'];
|
|
1290
|
+
}): Promise<void>;
|
|
1291
|
+
deleteFile(args: {
|
|
1292
|
+
path: string;
|
|
1293
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1294
|
+
}): Promise<void>;
|
|
1295
|
+
removeDirectory(args: {
|
|
1296
|
+
path: string;
|
|
1297
|
+
context: ASimpleCompositeSubfs['context'];
|
|
1298
|
+
}): Promise<void>;
|
|
1299
|
+
/**
|
|
1300
|
+
* Normalize a path to ensure consistent format
|
|
1301
|
+
*/
|
|
1302
|
+
private normalizePath;
|
|
1303
|
+
/**
|
|
1304
|
+
* Get the parent directory path
|
|
1305
|
+
*/
|
|
1306
|
+
private getParentPath;
|
|
1307
|
+
/**
|
|
1308
|
+
* Get the base name of a path
|
|
1309
|
+
*/
|
|
1310
|
+
private getBaseName;
|
|
1311
|
+
/**
|
|
1312
|
+
* Join path segments
|
|
1313
|
+
*/
|
|
1314
|
+
private joinPath;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
declare function toDirEntry(args: {
|
|
1318
|
+
parent: string;
|
|
1319
|
+
name: string;
|
|
1320
|
+
isDir: boolean;
|
|
1321
|
+
}): nodeFs.Dirent;
|
|
1322
|
+
|
|
1323
|
+
export { ASimpleCompositeSubfs, CompositeFs, CompositeSubFsAdapter, CopyOnWriteSubFs, EphemeralSubFs, HiddenFileSubFs, SimpleMemorySubFs, createBranchOperationsAdapter, createFsOperationFileLogger, createLegitSyncService, createLegitVirtualFileAdapter, getLegitFsAccess, mergeLegitRouteFolders, openLegitFs, openLegitFsWithMemoryFs, toDirEntry };
|
|
1324
|
+
export type { FileSystemData, FsOperationLogger, HistoryItem, Operation, User };
|