@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 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, PathLike } from 'memfs/lib/node/types/misc.js';
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
- readFile(path: nodeFs.PathLike | IFileHandle, options?: IReadFileOptions | string): Promise<TDataOut>;
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
- * open returns a CompositFsFileHandle that wraps the real filehandle from the responsible SubFs and allows
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 -> passThroughFileSystem
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
- gitRoot: string;
227
- ephemeralFilesFileSystem: CompositeSubFs | undefined;
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
- defaultBranch: string;
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, storageFs, gitRoot, defaultBranch, }: {
484
+ constructor({ name, filterLayers, routes, rootPath, }: {
242
485
  name: string;
243
- storageFs: typeof nodeFs;
244
- gitRoot: string;
245
- defaultBranch?: string;
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
- * helper function that takes a filePath and returns the fs that is responsible Sub filesystem for it
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, claudeHandler, }: {
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
- claudeHandler?: boolean;
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, parentFs, gitRoot, hiddenFiles, }: {
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, parentFs, gitRoot, ephemeralPatterns, }: {
697
+ constructor({ name, rootPath, ephemeralPatterns, }: {
483
698
  name: string;
484
- parentFs: CompositeFs;
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
- * FS utilized to provide pass-through access to the underlying filesystem
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 PassThroughSubFs extends BaseCompositeSubFs {
759
+ declare class CopyOnWriteSubFs extends BaseCompositeSubFs {
540
760
  private openFh;
541
- private memFs;
542
- private targetFs;
543
- constructor({ name, parentFs, gitRoot, }: {
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
- parentFs: CompositeFs;
546
- gitRoot: string;
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(path: PathLike$1, opts?: {
788
+ stat(statPath: PathLike$1, opts?: {
553
789
  bigint?: false;
554
790
  }): Promise<nodeFs.Stats>;
555
- stat(path: PathLike$1, opts: {
791
+ stat(statPath: PathLike$1, opts: {
556
792
  bigint: true;
557
793
  }): Promise<nodeFs.BigIntStats>;
558
- stat(path: PathLike$1, opts?: {
794
+ stat(statPath: PathLike$1, opts?: {
559
795
  bigint?: boolean;
560
796
  }): Promise<nodeFs.Stats | nodeFs.BigIntStats>;
561
- lstat(path: PathLike$1, opts?: {
797
+ lstat(lstatPath: PathLike$1, opts?: {
562
798
  bigint?: false;
563
799
  }): Promise<nodeFs.Stats>;
564
- lstat(path: PathLike$1, opts: {
800
+ lstat(lstatPath: PathLike$1, opts: {
565
801
  bigint: true;
566
802
  }): Promise<nodeFs.BigIntStats>;
567
- lstat(path: PathLike$1, opts?: {
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<CompositeFsDir>;
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(path: PathLike$1, ...args: any[]): Promise<any>;
574
- readlink(path: PathLike$1, ...args: any[]): Promise<any>;
575
- unlink(path: PathLike$1): Promise<void>;
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(path: PathLike$1, options?: nodeFs.RmDirOptions): Promise<void>;
578
- symlink(target: PathLike$1, path: PathLike$1, type?: string | null): Promise<void>;
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(path: PathLike$1 | IFileHandle, options?: IReadFileOptions | string): Promise<TDataOut>;
593
- writeFile(path: string, data: TData, options: IWriteFileOptions | string): Promise<void>;
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
- * Git-backed CompositeSubFs implementation.
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
- * docx file
664
- * - we unpack the docx and store xml files as blobs in git
665
- * mpeg file
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, parentFs, gitStorageFs, gitRoot, routerConfig, }: {
908
+ constructor({ name, gitStorageFs, gitRoot, handler, rootPath, }: {
681
909
  name: string;
682
- parentFs: CompositeFs;
683
910
  gitStorageFs: any;
684
911
  gitRoot: string;
685
- routerConfig: LegitRouteFolder;
912
+ handler: VirtualFileDefinition;
913
+ rootPath: string;
686
914
  });
687
915
  responsible(filePath: string): Promise<boolean>;
688
- private getRouteHandler;
689
916
  /**
690
- * Opens a virtual file from the Git-based virtual file system.
691
- *
692
- * This method retrieves a virtual file descriptor for the given `filePath`, checks if the file is writable
693
- * based on its type and the provided `flags`, and ensures that write operations are only allowed for
694
- * certain file types (e.g., "branch-file", "branch-head", "branch-tip"). It then loads the file's content
695
- * into the in-memory file system (`memFs`), ensures parent directories exist, and finally opens the file,
696
- * returning a `CompositFsFileHandle` for further operations.
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
- * @param filePath - The path to the virtual file to open.
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
- declare const gitBranchOperationsVirtualFile: VirtualFileDefinition;
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
- export { CompositeFs, EphemeralSubFs, GitSubFs, HiddenFileSubFs, PassThroughSubFs, createFsOperationFileLogger, createLegitSyncService, getLegitFsAccess, gitBranchOperationsVirtualFile, openLegitFs, openLegitFsWithMemoryFs };
816
- export type { FsOperationLogger, HistoryItem, Operation, User };
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 };