@fgv/ts-json-base 5.0.2 → 5.1.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/packlets/converters/converters.js +36 -14
  2. package/dist/packlets/file-tree/directoryItem.js +99 -4
  3. package/dist/packlets/file-tree/fileItem.js +47 -9
  4. package/dist/packlets/file-tree/fileTreeAccessors.js +59 -1
  5. package/dist/packlets/file-tree/filterSpec.js +74 -0
  6. package/dist/packlets/file-tree/fsTree.js +107 -12
  7. package/dist/packlets/file-tree/in-memory/inMemoryTree.js +279 -21
  8. package/dist/packlets/file-tree/in-memory/treeBuilder.js +31 -0
  9. package/dist/packlets/file-tree/index.browser.js +1 -0
  10. package/dist/packlets/file-tree/index.js +1 -0
  11. package/dist/packlets/json-file/file.js +1 -1
  12. package/dist/packlets/json-file/jsonFsHelper.js +1 -1
  13. package/dist/packlets/validators/validators.js +8 -8
  14. package/dist/ts-json-base.d.ts +439 -65
  15. package/dist/tsdoc-metadata.json +1 -1
  16. package/lib/packlets/converters/converters.d.ts +20 -13
  17. package/lib/packlets/converters/converters.js +36 -13
  18. package/lib/packlets/file-tree/directoryItem.d.ts +29 -6
  19. package/lib/packlets/file-tree/directoryItem.js +98 -3
  20. package/lib/packlets/file-tree/fileItem.d.ts +31 -14
  21. package/lib/packlets/file-tree/fileItem.js +46 -8
  22. package/lib/packlets/file-tree/fileTreeAccessors.d.ts +237 -3
  23. package/lib/packlets/file-tree/fileTreeAccessors.js +63 -0
  24. package/lib/packlets/file-tree/filterSpec.d.ts +10 -0
  25. package/lib/packlets/file-tree/filterSpec.js +77 -0
  26. package/lib/packlets/file-tree/fsTree.d.ts +37 -13
  27. package/lib/packlets/file-tree/fsTree.js +106 -11
  28. package/lib/packlets/file-tree/in-memory/inMemoryTree.d.ts +37 -13
  29. package/lib/packlets/file-tree/in-memory/inMemoryTree.js +278 -20
  30. package/lib/packlets/file-tree/in-memory/treeBuilder.d.ts +15 -0
  31. package/lib/packlets/file-tree/in-memory/treeBuilder.js +31 -0
  32. package/lib/packlets/file-tree/index.browser.d.ts +1 -0
  33. package/lib/packlets/file-tree/index.browser.js +1 -0
  34. package/lib/packlets/file-tree/index.d.ts +1 -0
  35. package/lib/packlets/file-tree/index.js +1 -0
  36. package/lib/packlets/json-file/file.d.ts +1 -1
  37. package/lib/packlets/json-file/file.js +1 -1
  38. package/lib/packlets/json-file/jsonFsHelper.d.ts +1 -1
  39. package/lib/packlets/json-file/jsonFsHelper.js +1 -1
  40. package/lib/packlets/validators/validators.d.ts +9 -9
  41. package/lib/packlets/validators/validators.js +8 -8
  42. package/package.json +29 -30
  43. package/dist/test/fixtures/file-tree/docs/api/reference.json +0 -1
@@ -1,6 +1,7 @@
1
1
  import { Conversion } from '@fgv/ts-utils';
2
2
  import { Converter } from '@fgv/ts-utils';
3
3
  import { Converters as Converters_3 } from '@fgv/ts-utils';
4
+ import { DetailedResult } from '@fgv/ts-utils';
4
5
  import { ObjectConverter as ObjectConverter_2 } from '@fgv/ts-utils';
5
6
  import { Result } from '@fgv/ts-utils';
6
7
  import { StringConverter } from '@fgv/ts-utils';
@@ -8,6 +9,30 @@ import { Validation } from '@fgv/ts-utils';
8
9
  import { Validator } from '@fgv/ts-utils';
9
10
  import { Validators as Validators_3 } from '@fgv/ts-utils';
10
11
 
12
+ /**
13
+ * A directory item that may or may not be mutable at runtime.
14
+ *
15
+ * Use this type for parameters or fields where the code checks for mutability
16
+ * and handles the read-only case gracefully. Use {@link FileTree.IMutableFileTreeDirectoryItem}
17
+ * when mutation is required.
18
+ *
19
+ * Narrow with {@link FileTree.isMutableDirectoryItem} to access mutation methods.
20
+ * @public
21
+ */
22
+ declare type AnyFileTreeDirectoryItem<TCT extends string = string> = IFileTreeDirectoryItem<TCT> | IMutableFileTreeDirectoryItem<TCT>;
23
+
24
+ /**
25
+ * A file item that may or may not be mutable at runtime.
26
+ *
27
+ * Use this type for parameters or fields where the code checks for mutability
28
+ * and handles the read-only case gracefully. Use {@link FileTree.IMutableFileTreeFileItem}
29
+ * when mutation is required.
30
+ *
31
+ * Narrow with {@link FileTree.isMutableFileItem} to access mutation methods.
32
+ * @public
33
+ */
34
+ declare type AnyFileTreeFileItem<TCT extends string = string> = IFileTreeFileItem<TCT> | IMutableFileTreeFileItem<TCT>;
35
+
11
36
  /**
12
37
  * A converter which converts a supplied `unknown` value to a valid array of {@link JsonCompatibleType | JsonCompatible} values.
13
38
  * @public
@@ -40,16 +65,16 @@ declare function arrayOf_2<T, TC = unknown>(validateElement: JsonCompatible_2.Va
40
65
  declare type ArrayValidator<T, TC = unknown> = Validation.Classes.ArrayValidator<JsonCompatibleType<T>, TC>;
41
66
 
42
67
  /**
43
- * A {@link Converter | Converter} which converts `unknown` to a `boolean`.
44
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
68
+ * A `Converter` which converts `unknown` to a `boolean`.
69
+ * Accepts `IJsonConverterContext` but ignores it.
45
70
  * Mirrors the behavior of `@fgv/ts-utils`.
46
71
  * @public
47
72
  */
48
73
  declare const boolean: Converter<boolean, IJsonConverterContext>;
49
74
 
50
75
  /**
51
- * A {@link Validation.Classes.BooleanValidator | BooleanValidator} which validates a boolean in place.
52
- * Accepts {@link Validators.IJsonValidatorContext | IJsonValidatorContext} but ignores it.
76
+ * A `BooleanValidator` which validates a boolean in place.
77
+ * Accepts `IJsonValidatorContext` but ignores it.
53
78
  * @public
54
79
  */
55
80
  declare const boolean_2: Validator<boolean, IJsonValidatorContext>;
@@ -80,6 +105,7 @@ declare namespace Converters {
80
105
  export {
81
106
  literal,
82
107
  enumeratedValue,
108
+ jsonConverter,
83
109
  IJsonConverterContext,
84
110
  jsonPrimitive,
85
111
  jsonObject,
@@ -156,17 +182,17 @@ declare const DefaultJsonTreeHelper: JsonTreeHelper;
156
182
  * Class representing a directory in a file tree.
157
183
  * @public
158
184
  */
159
- declare class DirectoryItem<TCT extends string = string> implements IFileTreeDirectoryItem<TCT> {
185
+ declare class DirectoryItem<TCT extends string = string> implements IMutableFileTreeDirectoryItem<TCT> {
160
186
  /**
161
- * {@inheritdoc FileTree.IFileTreeDirectoryItem."type"}
187
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem."type"}
162
188
  */
163
189
  readonly type: 'directory';
164
190
  /**
165
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.absolutePath}
191
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.absolutePath}
166
192
  */
167
193
  readonly absolutePath: string;
168
194
  /**
169
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.name}
195
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.name}
170
196
  */
171
197
  get name(): string;
172
198
  /**
@@ -192,9 +218,32 @@ declare class DirectoryItem<TCT extends string = string> implements IFileTreeDir
192
218
  */
193
219
  static create<TCT extends string = string>(path: string, hal: IFileTreeAccessors<TCT>): Result<DirectoryItem<TCT>>;
194
220
  /**
195
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.getChildren}
221
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.getChildren}
196
222
  */
197
223
  getChildren(): Result<ReadonlyArray<FileTreeItem<TCT>>>;
224
+ /**
225
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.createChildFile}
226
+ */
227
+ createChildFile(name: string, contents: string): Result<IMutableFileTreeFileItem<TCT>>;
228
+ /**
229
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.createChildDirectory}
230
+ */
231
+ createChildDirectory(name: string): Result<IMutableFileTreeDirectoryItem<TCT>>;
232
+ /**
233
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.deleteChild}
234
+ */
235
+ deleteChild(name: string, options?: IDeleteChildOptions): Result<boolean>;
236
+ /**
237
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.delete}
238
+ */
239
+ delete(): Result<boolean>;
240
+ /**
241
+ * Recursively deletes all children of a directory and then the directory itself.
242
+ * @param dirPath - The absolute path of the directory to delete.
243
+ * @returns `Success` with `true` if the directory was deleted, or `Failure` with an error message.
244
+ * @internal
245
+ */
246
+ private _deleteRecursive;
198
247
  }
199
248
 
200
249
  /**
@@ -209,33 +258,33 @@ declare class DirectoryItem<TCT extends string = string> implements IFileTreeDir
209
258
  declare function discriminatedObject<T, TD extends string = string, TC = unknown>(discriminatorProp: string, converters: Converters_3.DiscriminatedObjectConverters<JsonCompatibleType<T>, TD, TC>): JsonCompatible_2.Converter<T, TC>;
210
259
 
211
260
  /**
212
- * Helper function to create a {@link Converter | Converter} which converts `unknown` to one of a set of
261
+ * Helper function to create a `Converter` which converts `unknown` to one of a set of
213
262
  * supplied enumerated values. Anything else fails.
214
263
  *
215
264
  * @remarks
216
- * This JSON variant accepts an {@link Converters.IJsonConverterContext | IJsonConverterContext} OR
265
+ * This JSON variant accepts an `IJsonConverterContext` OR
217
266
  * a `ReadonlyArray<T>` as its conversion context. If the context is an array, it is used to override the
218
267
  * allowed values for that conversion; otherwise, the original `values` supplied at creation time are used.
219
268
  *
220
269
  * @param values - Array of allowed values.
221
270
  * @param message - Optional custom failure message.
222
- * @returns A new {@link Converter | Converter} returning `<T>`.
271
+ * @returns A new `Converter` returning `<T>`.
223
272
  * @public
224
273
  */
225
274
  declare function enumeratedValue<T>(values: ReadonlyArray<T>, message?: string): Converter<T, IJsonConverterContext | ReadonlyArray<T>>;
226
275
 
227
276
  /**
228
- * Helper function to create a {@link Validator | Validator} which validates `unknown` to one of a set of
277
+ * Helper function to create a `Validator` which validates `unknown` to one of a set of
229
278
  * supplied enumerated values. Anything else fails.
230
279
  *
231
280
  * @remarks
232
- * This JSON variant accepts an {@link Validators.IJsonValidatorContext | IJsonValidatorContext} OR
281
+ * This JSON variant accepts an `IJsonValidatorContext` OR
233
282
  * a `ReadonlyArray<T>` as its validation context. If the context is an array, it is used to override the
234
283
  * allowed values for that validation; otherwise, the original `values` supplied at creation time are used.
235
284
  *
236
285
  * @param values - Array of allowed values.
237
286
  * @param message - Optional custom failure message.
238
- * @returns A new {@link Validator | Validator} returning `<T>`.
287
+ * @returns A new `Validator` returning `<T>`.
239
288
  * @public
240
289
  */
241
290
  declare function enumeratedValue_2<T>(values: ReadonlyArray<T>, message?: string): Validator<T, IJsonValidatorContext | ReadonlyArray<T>>;
@@ -244,29 +293,29 @@ declare function enumeratedValue_2<T>(values: ReadonlyArray<T>, message?: string
244
293
  * Class representing a file in a file tree.
245
294
  * @public
246
295
  */
247
- declare class FileItem<TCT extends string = string> implements IFileTreeFileItem<TCT> {
296
+ declare class FileItem<TCT extends string = string> implements IMutableFileTreeFileItem<TCT> {
248
297
  /**
249
- * {@inheritdoc FileTree.IFileTreeFileItem."type"}
298
+ * {@inheritDoc FileTree.IFileTreeFileItem."type"}
250
299
  */
251
300
  readonly type: 'file';
252
301
  /**
253
- * {@inheritdoc FileTree.IFileTreeFileItem.absolutePath}
302
+ * {@inheritDoc FileTree.IFileTreeFileItem.absolutePath}
254
303
  */
255
304
  readonly absolutePath: string;
256
305
  /**
257
- * {@inheritdoc FileTree.IFileTreeFileItem.name}
306
+ * {@inheritDoc FileTree.IFileTreeFileItem.name}
258
307
  */
259
308
  get name(): string;
260
309
  /**
261
- * {@inheritdoc FileTree.IFileTreeFileItem.baseName}
310
+ * {@inheritDoc FileTree.IFileTreeFileItem.baseName}
262
311
  */
263
312
  get baseName(): string;
264
313
  /**
265
- * {@inheritdoc FileTree.IFileTreeFileItem.extension}
314
+ * {@inheritDoc FileTree.IFileTreeFileItem.extension}
266
315
  */
267
316
  get extension(): string;
268
317
  /**
269
- * {@inheritdoc FileTree.IFileTreeFileItem.contentType}
318
+ * {@inheritDoc FileTree.IFileTreeFileItem.contentType}
270
319
  */
271
320
  get contentType(): TCT | undefined;
272
321
  /**
@@ -296,15 +345,19 @@ declare class FileItem<TCT extends string = string> implements IFileTreeFileItem
296
345
  */
297
346
  static create<TCT extends string = string>(path: string, hal: IFileTreeAccessors<TCT>): Result<FileItem<TCT>>;
298
347
  /**
299
- * {@inheritdoc FileTree.IFileTreeFileItem.(getContents:1)}
348
+ * {@inheritDoc FileTree.IFileTreeFileItem.getIsMutable}
349
+ */
350
+ getIsMutable(): DetailedResult<boolean, SaveDetail>;
351
+ /**
352
+ * {@inheritDoc FileTree.IFileTreeFileItem.getContents}
300
353
  */
301
354
  getContents(): Result<JsonValue>;
302
355
  /**
303
- * {@inheritdoc FileTree.IFileTreeFileItem.(getContents:2)}
356
+ * {@inheritDoc FileTree.IFileTreeFileItem.getContents}
304
357
  */
305
358
  getContents<T>(converter: Validator<T> | Converter<T>): Result<T>;
306
359
  /**
307
- * {@inheritdoc FileTree.IFileTreeFileItem.getRawContents}
360
+ * {@inheritDoc FileTree.IFileTreeFileItem.getRawContents}
308
361
  */
309
362
  getRawContents(): Result<string>;
310
363
  /**
@@ -312,15 +365,28 @@ declare class FileItem<TCT extends string = string> implements IFileTreeFileItem
312
365
  * @param contentType - The content type of the file.
313
366
  */
314
367
  setContentType(contentType: TCT | undefined): void;
368
+ /**
369
+ * {@inheritDoc FileTree.IFileTreeFileItem.setContents}
370
+ */
371
+ setContents(json: JsonValue): Result<JsonValue>;
372
+ /**
373
+ * {@inheritDoc FileTree.IFileTreeFileItem.setRawContents}
374
+ */
375
+ setRawContents(contents: string): Result<string>;
376
+ /**
377
+ * {@inheritDoc FileTree.IFileTreeFileItem.delete}
378
+ */
379
+ delete(): Result<boolean>;
315
380
  /**
316
381
  * Default function to infer the content type of a file.
317
382
  * @param filePath - The path of the file.
383
+ * @param provided - Optional supplied content type.
318
384
  * @returns `Success` with the content type of the file if successful, or
319
385
  * `Failure` with an error message otherwise.
320
386
  * @remarks This default implementation always returns `Success` with `undefined`.
321
387
  * @public
322
388
  */
323
- static defaultInferContentType<TCT extends string = string>(__filePath: string, __provided?: string): Result<TCT | undefined>;
389
+ static defaultInferContentType<TCT extends string = string>(filePath: string, provided?: string): Result<TCT | undefined>;
324
390
  /**
325
391
  * Default function to accept the content type of a file.
326
392
  * @param filePath - The path of the file.
@@ -330,22 +396,39 @@ declare class FileItem<TCT extends string = string> implements IFileTreeFileItem
330
396
  * @remarks This default implementation always returns `Success` with `undefined`.
331
397
  * @public
332
398
  */
333
- static defaultAcceptContentType<TCT extends string = string>(__filePath: string, provided?: TCT): Result<TCT | undefined>;
399
+ static defaultAcceptContentType<TCT extends string = string>(filePath: string, provided?: TCT): Result<TCT | undefined>;
334
400
  }
335
401
 
336
402
  declare namespace FileTree {
337
403
  export {
338
404
  inMemory,
405
+ isMutableAccessors,
406
+ isPersistentAccessors,
407
+ isMutableFileItem,
408
+ isMutableDirectoryItem,
409
+ SaveCapability,
410
+ SaveFailureReason,
411
+ SaveDetail,
412
+ IFilterSpec,
339
413
  FileTreeItemType,
340
414
  ContentTypeFactory,
341
415
  IFileTreeInitParams,
416
+ IDeleteChildOptions,
342
417
  IFileTreeFileItem,
418
+ IMutableFileTreeFileItem,
343
419
  IFileTreeDirectoryItem,
420
+ IMutableFileTreeDirectoryItem,
344
421
  FileTreeItem,
422
+ MutableFileTreeItem,
423
+ AnyFileTreeFileItem,
424
+ AnyFileTreeDirectoryItem,
345
425
  IFileTreeAccessors,
426
+ IMutableFileTreeAccessors,
427
+ IPersistentFileTreeAccessors,
346
428
  FileTree_2 as FileTree,
347
429
  DirectoryItem,
348
430
  FileItem,
431
+ isPathMutable,
349
432
  forFilesystem,
350
433
  IInMemoryFile,
351
434
  InMemoryTreeAccessors,
@@ -434,11 +517,11 @@ declare function forFilesystem<TCT extends string = string>(prefix?: string): Re
434
517
  declare function forFilesystem<TCT extends string = string>(params?: IFileTreeInitParams<TCT>): Result<FileTree_2<TCT>>;
435
518
 
436
519
  /**
437
- * Implementation of {@link FileTree.IFileTreeAccessors} that uses the
438
- * file system to access files and directories.
520
+ * Implementation of {@link FileTree.IMutableFileTreeAccessors} that uses the
521
+ * file system to access and modify files and directories.
439
522
  * @public
440
523
  */
441
- declare class FsFileTreeAccessors<TCT extends string = string> implements IFileTreeAccessors<TCT> {
524
+ declare class FsFileTreeAccessors<TCT extends string = string> implements IMutableFileTreeAccessors<TCT> {
442
525
  /**
443
526
  * Optional path prefix to prepend to all paths.
444
527
  */
@@ -448,6 +531,10 @@ declare class FsFileTreeAccessors<TCT extends string = string> implements IFileT
448
531
  * @public
449
532
  */
450
533
  protected readonly _inferContentType: (filePath: string) => Result<TCT | undefined>;
534
+ /**
535
+ * The mutability configuration.
536
+ */
537
+ private readonly _mutable;
451
538
  /**
452
539
  * Construct a new instance of the {@link FileTree.FsFileTreeAccessors | FsFileTreeAccessors} class.
453
540
  * @param params - Optional {@link FileTree.IFileTreeInitParams | initialization parameters}.
@@ -455,37 +542,69 @@ declare class FsFileTreeAccessors<TCT extends string = string> implements IFileT
455
542
  */
456
543
  constructor(params?: IFileTreeInitParams<TCT>);
457
544
  /**
458
- * {@inheritdoc FileTree.IFileTreeAccessors.resolveAbsolutePath}
545
+ * {@inheritDoc FileTree.IFileTreeAccessors.resolveAbsolutePath}
459
546
  */
460
547
  resolveAbsolutePath(...paths: string[]): string;
461
548
  /**
462
- * {@inheritdoc FileTree.IFileTreeAccessors.getExtension}
549
+ * {@inheritDoc FileTree.IFileTreeAccessors.getExtension}
463
550
  */
464
551
  getExtension(itemPath: string): string;
465
552
  /**
466
- * {@inheritdoc FileTree.IFileTreeAccessors.getBaseName}
553
+ * {@inheritDoc FileTree.IFileTreeAccessors.getBaseName}
467
554
  */
468
555
  getBaseName(itemPath: string, suffix?: string): string;
469
556
  /**
470
- * {@inheritdoc FileTree.IFileTreeAccessors.joinPaths}
557
+ * {@inheritDoc FileTree.IFileTreeAccessors.joinPaths}
471
558
  */
472
559
  joinPaths(...paths: string[]): string;
473
560
  /**
474
- * {@inheritdoc FileTree.IFileTreeAccessors.getItem}
561
+ * {@inheritDoc FileTree.IFileTreeAccessors.getItem}
475
562
  */
476
563
  getItem(itemPath: string): Result<FileTreeItem<TCT>>;
477
564
  /**
478
- * {@inheritdoc FileTree.IFileTreeAccessors.getFileContents}
565
+ * {@inheritDoc FileTree.IFileTreeAccessors.getFileContents}
479
566
  */
480
567
  getFileContents(filePath: string): Result<string>;
481
568
  /**
482
- * {@inheritdoc FileTree.IFileTreeAccessors.getFileContentType}
569
+ * {@inheritDoc FileTree.IFileTreeAccessors.getFileContentType}
483
570
  */
484
571
  getFileContentType(filePath: string, provided?: string): Result<TCT | undefined>;
485
572
  /**
486
- * {@inheritdoc FileTree.IFileTreeAccessors.getChildren}
573
+ * {@inheritDoc FileTree.IFileTreeAccessors.getChildren}
487
574
  */
488
575
  getChildren(dirPath: string): Result<ReadonlyArray<FileTreeItem<TCT>>>;
576
+ /**
577
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.fileIsMutable}
578
+ */
579
+ fileIsMutable(path: string): DetailedResult<boolean, SaveDetail>;
580
+ /**
581
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.saveFileContents}
582
+ */
583
+ saveFileContents(path: string, contents: string): Result<string>;
584
+ /**
585
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.deleteFile}
586
+ */
587
+ deleteFile(path: string): Result<boolean>;
588
+ /**
589
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.createDirectory}
590
+ */
591
+ createDirectory(dirPath: string): Result<string>;
592
+ /**
593
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.deleteDirectory}
594
+ */
595
+ deleteDirectory(dirPath: string): Result<boolean>;
596
+ }
597
+
598
+ /**
599
+ * Options for deleting a child item from a directory.
600
+ * @public
601
+ */
602
+ declare interface IDeleteChildOptions {
603
+ /**
604
+ * If true, recursively delete directory children and their contents.
605
+ * Default: false (fail if directory is non-empty).
606
+ */
607
+ recursive?: boolean;
489
608
  }
490
609
 
491
610
  /**
@@ -547,7 +666,7 @@ declare interface IFileTreeAccessors<TCT extends string = string> {
547
666
  }
548
667
 
549
668
  /**
550
- * Interface for a directory in a file tree.
669
+ * Interface for a read-only directory in a file tree.
551
670
  * @public
552
671
  */
553
672
  declare interface IFileTreeDirectoryItem<TCT extends string = string> {
@@ -572,7 +691,7 @@ declare interface IFileTreeDirectoryItem<TCT extends string = string> {
572
691
  }
573
692
 
574
693
  /**
575
- * Interface for a file in a file tree.
694
+ * Interface for a read-only file in a file tree.
576
695
  * @public
577
696
  */
578
697
  declare interface IFileTreeFileItem<TCT extends string = string> {
@@ -629,6 +748,28 @@ declare interface IFileTreeFileItem<TCT extends string = string> {
629
748
  declare interface IFileTreeInitParams<TCT extends string = string> {
630
749
  prefix?: string;
631
750
  inferContentType?: ContentTypeFactory<TCT>;
751
+ /**
752
+ * Controls mutability of the file tree.
753
+ * - `undefined` or `false`: No files are mutable.
754
+ * - `true`: All files are mutable.
755
+ * - `IFilterSpec`: Only files matching the filter are mutable.
756
+ */
757
+ mutable?: boolean | IFilterSpec;
758
+ }
759
+
760
+ /**
761
+ * Filter specification for controlling which paths are mutable.
762
+ * @public
763
+ */
764
+ declare interface IFilterSpec {
765
+ /**
766
+ * Paths or patterns to include. If specified, only matching paths are mutable.
767
+ */
768
+ include?: (string | RegExp)[];
769
+ /**
770
+ * Paths or patterns to exclude. Matching paths are not mutable.
771
+ */
772
+ exclude?: (string | RegExp)[];
632
773
  }
633
774
 
634
775
  /**
@@ -708,6 +849,113 @@ declare interface IJsonValidatorContext {
708
849
  ignoreUndefinedProperties?: boolean;
709
850
  }
710
851
 
852
+ /**
853
+ * Extended accessors interface that supports mutation operations.
854
+ * All mutation methods are required — use {@link FileTree.isMutableAccessors | isMutableAccessors}
855
+ * type guard to check if an accessor supports mutation.
856
+ * @public
857
+ */
858
+ declare interface IMutableFileTreeAccessors<TCT extends string = string> extends IFileTreeAccessors<TCT> {
859
+ /**
860
+ * Checks if a file at the given path can be saved.
861
+ * @param path - The path to check.
862
+ * @returns `DetailedSuccess` with {@link FileTree.SaveCapability} if the file can be saved,
863
+ * or `DetailedFailure` with {@link FileTree.SaveFailureReason} if it cannot.
864
+ */
865
+ fileIsMutable(path: string): DetailedResult<boolean, SaveDetail>;
866
+ /**
867
+ * Saves the contents to a file at the given path.
868
+ * @param path - The path of the file to save.
869
+ * @param contents - The string contents to save.
870
+ * @returns `Success` if the file was saved, or `Failure` with an error message.
871
+ */
872
+ saveFileContents(path: string, contents: string): Result<string>;
873
+ /**
874
+ * Deletes a file at the given path.
875
+ * @param path - The path of the file to delete.
876
+ * @returns `Success` with `true` if the file was deleted, or `Failure` with an error message.
877
+ */
878
+ deleteFile(path: string): Result<boolean>;
879
+ /**
880
+ * Creates a directory at the given path, including any missing parent directories.
881
+ * @param path - The path of the directory to create.
882
+ * @returns `Success` with the absolute path if created, or `Failure` with an error message.
883
+ */
884
+ createDirectory(path: string): Result<string>;
885
+ /**
886
+ * Deletes a directory at the given path.
887
+ * The directory must be empty or the operation will fail.
888
+ * @param path - The path of the directory to delete.
889
+ * @returns `Success` with `true` if the directory was deleted, or `Failure` with an error message.
890
+ */
891
+ deleteDirectory(path: string): Result<boolean>;
892
+ }
893
+
894
+ /**
895
+ * Extended directory item interface that supports mutation operations.
896
+ * Use {@link FileTree.isMutableDirectoryItem | isMutableDirectoryItem} type guard to narrow.
897
+ * @public
898
+ */
899
+ declare interface IMutableFileTreeDirectoryItem<TCT extends string = string> extends IFileTreeDirectoryItem<TCT> {
900
+ /**
901
+ * Creates a new file as a child of this directory.
902
+ * @param name - The file name to create.
903
+ * @param contents - The string contents to write.
904
+ * @returns `Success` with the new file item, or `Failure` with an error message.
905
+ */
906
+ createChildFile(name: string, contents: string): Result<IMutableFileTreeFileItem<TCT>>;
907
+ /**
908
+ * Creates a new subdirectory as a child of this directory.
909
+ * @param name - The directory name to create.
910
+ * @returns `Success` with the new directory item, or `Failure` with an error message.
911
+ */
912
+ createChildDirectory(name: string): Result<IMutableFileTreeDirectoryItem<TCT>>;
913
+ /**
914
+ * Deletes a child item from this directory.
915
+ * @param name - The name of the child to delete.
916
+ * @param options - Optional {@link FileTree.IDeleteChildOptions | options} controlling deletion behavior.
917
+ * @returns `Success` with `true` if the child was deleted, or `Failure` with an error message.
918
+ */
919
+ deleteChild(name: string, options?: IDeleteChildOptions): Result<boolean>;
920
+ /**
921
+ * Deletes this directory from its backing store.
922
+ * The directory must be empty or the operation will fail.
923
+ * @returns `Success` with `true` if the directory was deleted, or `Failure` with an error message.
924
+ */
925
+ delete(): Result<boolean>;
926
+ }
927
+
928
+ /**
929
+ * Extended file item interface that supports mutation operations.
930
+ * Use {@link FileTree.isMutableFileItem | isMutableFileItem} type guard to narrow.
931
+ * @public
932
+ */
933
+ declare interface IMutableFileTreeFileItem<TCT extends string = string> extends IFileTreeFileItem<TCT> {
934
+ /**
935
+ * Indicates whether this file can be saved.
936
+ * @returns `DetailedSuccess` with {@link FileTree.SaveCapability} if the file can be saved,
937
+ * or `DetailedFailure` with {@link FileTree.SaveFailureReason} if it cannot.
938
+ */
939
+ getIsMutable(): DetailedResult<boolean, SaveDetail>;
940
+ /**
941
+ * Sets the contents of the file from a JSON value.
942
+ * @param json - The JSON value to serialize and save.
943
+ * @returns `Success` if the file was saved, or `Failure` with an error message.
944
+ */
945
+ setContents(json: JsonValue): Result<JsonValue>;
946
+ /**
947
+ * Sets the raw contents of the file.
948
+ * @param contents - The string contents to save.
949
+ * @returns `Success` if the file was saved, or `Failure` with an error message.
950
+ */
951
+ setRawContents(contents: string): Result<string>;
952
+ /**
953
+ * Deletes this file from its backing store.
954
+ * @returns `Success` with `true` if the file was deleted, or `Failure` with an error message.
955
+ */
956
+ delete(): Result<boolean>;
957
+ }
958
+
711
959
  /**
712
960
  * Helper function to create a new {@link FileTree.FileTree | FileTree} instance
713
961
  * with accessors for an in-memory file tree.
@@ -731,13 +979,16 @@ declare function inMemory<TCT extends string = string>(files: IInMemoryFile<TCT>
731
979
  declare function inMemory<TCT extends string = string>(files: IInMemoryFile<TCT>[], params?: IFileTreeInitParams<TCT>): Result<FileTree_2<TCT>>;
732
980
 
733
981
  /**
734
- * Implementation of {@link FileTree.IFileTreeAccessors} that uses an in-memory
735
- * tree to access files and directories.
982
+ * Implementation of {@link FileTree.IMutableFileTreeAccessors} that uses an in-memory
983
+ * tree to access and modify files and directories.
736
984
  * @public
737
985
  */
738
- declare class InMemoryTreeAccessors<TCT extends string = string> implements IFileTreeAccessors<TCT> {
986
+ declare class InMemoryTreeAccessors<TCT extends string = string> implements IMutableFileTreeAccessors<TCT> {
739
987
  private readonly _tree;
740
988
  private readonly _inferContentType;
989
+ private readonly _mutable;
990
+ private readonly _mutableByPath;
991
+ private readonly _mutableRoot;
741
992
  /**
742
993
  * Protected constructor for derived classes.
743
994
  * @param files - An array of {@link FileTree.IInMemoryFile | in-memory files} to include in the tree.
@@ -760,37 +1011,80 @@ declare class InMemoryTreeAccessors<TCT extends string = string> implements IFil
760
1011
  */
761
1012
  static create<TCT extends string = string>(files: IInMemoryFile<TCT>[], params?: IFileTreeInitParams<TCT>): Result<InMemoryTreeAccessors<TCT>>;
762
1013
  /**
763
- * {@inheritdoc FileTree.IFileTreeAccessors.resolveAbsolutePath}
1014
+ * {@inheritDoc FileTree.IFileTreeAccessors.resolveAbsolutePath}
764
1015
  */
765
1016
  resolveAbsolutePath(...paths: string[]): string;
766
1017
  /**
767
- * {@inheritdoc FileTree.IFileTreeAccessors.getExtension}
1018
+ * {@inheritDoc FileTree.IFileTreeAccessors.getExtension}
768
1019
  */
769
1020
  getExtension(path: string): string;
770
1021
  /**
771
- * {@inheritdoc FileTree.IFileTreeAccessors.getBaseName}
1022
+ * {@inheritDoc FileTree.IFileTreeAccessors.getBaseName}
772
1023
  */
773
1024
  getBaseName(path: string, suffix?: string): string;
774
1025
  /**
775
- * {@inheritdoc FileTree.IFileTreeAccessors.joinPaths}
1026
+ * {@inheritDoc FileTree.IFileTreeAccessors.joinPaths}
776
1027
  */
777
1028
  joinPaths(...paths: string[]): string;
778
1029
  /**
779
- * {@inheritdoc FileTree.IFileTreeAccessors.getItem}
1030
+ * {@inheritDoc FileTree.IFileTreeAccessors.getItem}
780
1031
  */
781
1032
  getItem(itemPath: string): Result<FileTreeItem<TCT>>;
782
1033
  /**
783
- * {@inheritdoc FileTree.IFileTreeAccessors.getFileContents}
1034
+ * {@inheritDoc FileTree.IFileTreeAccessors.getFileContents}
784
1035
  */
785
1036
  getFileContents(path: string): Result<string>;
786
1037
  /**
787
- * {@inheritdoc FileTree.IFileTreeAccessors.getFileContentType}
1038
+ * {@inheritDoc FileTree.IFileTreeAccessors.getFileContentType}
788
1039
  */
789
1040
  getFileContentType(path: string, provided?: string): Result<TCT | undefined>;
790
1041
  /**
791
- * {@inheritdoc FileTree.IFileTreeAccessors.getChildren}
1042
+ * {@inheritDoc FileTree.IFileTreeAccessors.getChildren}
792
1043
  */
793
1044
  getChildren(path: string): Result<ReadonlyArray<FileTreeItem<TCT>>>;
1045
+ private _addMutableFile;
1046
+ /**
1047
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.createDirectory}
1048
+ */
1049
+ createDirectory(dirPath: string): Result<string>;
1050
+ /**
1051
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.fileIsMutable}
1052
+ */
1053
+ fileIsMutable(path: string): DetailedResult<boolean, SaveDetail>;
1054
+ /**
1055
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.deleteFile}
1056
+ */
1057
+ deleteFile(path: string): Result<boolean>;
1058
+ /**
1059
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.deleteDirectory}
1060
+ */
1061
+ deleteDirectory(path: string): Result<boolean>;
1062
+ /**
1063
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.saveFileContents}
1064
+ */
1065
+ saveFileContents(path: string, contents: string): Result<string>;
1066
+ }
1067
+
1068
+ /**
1069
+ * Extended accessors interface that supports persistence operations.
1070
+ * @public
1071
+ */
1072
+ declare interface IPersistentFileTreeAccessors<TCT extends string = string> extends IMutableFileTreeAccessors<TCT> {
1073
+ /**
1074
+ * Synchronize all dirty files to persistent storage.
1075
+ * @returns Promise resolving to success or failure
1076
+ */
1077
+ syncToDisk(): Promise<Result<void>>;
1078
+ /**
1079
+ * Check if there are unsaved changes.
1080
+ * @returns True if there are dirty files
1081
+ */
1082
+ isDirty(): boolean;
1083
+ /**
1084
+ * Get paths of all files with unsaved changes.
1085
+ * @returns Array of dirty file paths
1086
+ */
1087
+ getDirtyPaths(): string[];
794
1088
  }
795
1089
 
796
1090
  /**
@@ -835,6 +1129,47 @@ export declare function isJsonObject(from: unknown): from is JsonObject;
835
1129
  */
836
1130
  export declare function isJsonPrimitive(from: unknown): from is JsonPrimitive;
837
1131
 
1132
+ /**
1133
+ * Type guard to check if accessors support mutation.
1134
+ * @param accessors - The accessors to check.
1135
+ * @returns `true` if the accessors implement {@link FileTree.IMutableFileTreeAccessors}.
1136
+ * @public
1137
+ */
1138
+ declare function isMutableAccessors<TCT extends string = string>(accessors: IFileTreeAccessors<TCT>): accessors is IMutableFileTreeAccessors<TCT>;
1139
+
1140
+ /**
1141
+ * Type guard to check if a directory item supports mutation.
1142
+ * @param item - The directory item to check.
1143
+ * @returns `true` if the item implements {@link FileTree.IMutableFileTreeDirectoryItem}.
1144
+ * @public
1145
+ */
1146
+ declare function isMutableDirectoryItem<TCT extends string = string>(item: AnyFileTreeDirectoryItem<TCT> | FileTreeItem<TCT>): item is IMutableFileTreeDirectoryItem<TCT>;
1147
+
1148
+ /**
1149
+ * Type guard to check if a file item supports mutation.
1150
+ * @param item - The file item to check.
1151
+ * @returns `true` if the item implements {@link FileTree.IMutableFileTreeFileItem}.
1152
+ * @public
1153
+ */
1154
+ declare function isMutableFileItem<TCT extends string = string>(item: AnyFileTreeFileItem<TCT> | FileTreeItem<TCT>): item is IMutableFileTreeFileItem<TCT>;
1155
+
1156
+ /**
1157
+ * Checks if a path is allowed by a mutability configuration.
1158
+ * @param path - The path to check.
1159
+ * @param mutable - The mutability configuration.
1160
+ * @returns `true` if the path is mutable according to the configuration.
1161
+ * @public
1162
+ */
1163
+ declare function isPathMutable(path: string, mutable: boolean | IFilterSpec | undefined): boolean;
1164
+
1165
+ /**
1166
+ * Type guard to check if accessors support persistence.
1167
+ * @param accessors - The accessors to check.
1168
+ * @returns `true` if the accessors implement {@link FileTree.IPersistentFileTreeAccessors}.
1169
+ * @public
1170
+ */
1171
+ declare function isPersistentAccessors<TCT extends string = string>(accessors: IFileTreeAccessors<TCT>): accessors is IPersistentFileTreeAccessors<TCT>;
1172
+
838
1173
  /**
839
1174
  * Helper type to detect if T is exactly unknown.
840
1175
  */
@@ -858,7 +1193,7 @@ export declare interface JsonArray extends Array<JsonValue> {
858
1193
  * An copying converter which converts a supplied `unknown` value to
859
1194
  * a valid {@link JsonArray | JsonArray}. Fails by default if any properties or array elements
860
1195
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
861
- * {@link Converters.IJsonConverterContext | context} at runtime.
1196
+ * `IJsonConverterContext` at runtime.
862
1197
  *
863
1198
  * Guaranteed to return a new array.
864
1199
  * @public
@@ -954,6 +1289,14 @@ export declare type JsonCompatibleType<T> = IsUnknown<T> extends true ? JsonValu
954
1289
  [K in keyof T]: JsonCompatibleType<T[K]>;
955
1290
  } : ['Error: Non-JSON type'];
956
1291
 
1292
+ /**
1293
+ * Creates a converter that parses JSON string content and then applies the supplied converter.
1294
+ * @param converter - Converter to apply to the parsed JSON
1295
+ * @returns Converter that parses JSON then validates
1296
+ * @public
1297
+ */
1298
+ declare function jsonConverter<T>(converter: Converter<T>): Converter<T>;
1299
+
957
1300
  declare namespace JsonFile {
958
1301
  export {
959
1302
  readJsonFileSync,
@@ -994,7 +1337,7 @@ declare class JsonFsHelper {
994
1337
  readonly config: IJsonFsHelperConfig;
995
1338
  /**
996
1339
  * Construct a new {@link JsonFile.JsonFsHelper | JsonFsHelper}.
997
- * @param json - Optional {@link JsonFile.IJsonLike | IJsonLike} used to process strings
1340
+ * @param init - Optional {@link JsonFile.JsonFsHelperInitOptions | init options} to construct
998
1341
  * and JSON values.
999
1342
  */
1000
1343
  constructor(init?: JsonFsHelperInitOptions);
@@ -1057,7 +1400,7 @@ export declare interface JsonObject {
1057
1400
  * An copying converter which converts a supplied `unknown` value into
1058
1401
  * a valid {@link JsonObject | JsonObject}. Fails by default if any properties or array elements
1059
1402
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
1060
- * {@link Converters.IJsonConverterContext | context} at runtime.
1403
+ * `IJsonConverterContext` at runtime.
1061
1404
  *
1062
1405
  * Guaranteed to return a new object.
1063
1406
  * @public
@@ -1179,7 +1522,7 @@ export declare type JsonValue = JsonPrimitive | JsonObject | JsonArray;
1179
1522
  * An copying converter which converts a supplied `unknown` value to a
1180
1523
  * valid {@link JsonValue | JsonValue}. Fails by default if any properties or array elements
1181
1524
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
1182
- * {@link Converters.IJsonConverterContext | context} at runtime.
1525
+ * `IJsonConverterContext` at runtime.
1183
1526
  * @public
1184
1527
  */
1185
1528
  declare const jsonValue: Converter<JsonValue, IJsonConverterContext>;
@@ -1201,7 +1544,7 @@ export declare type JsonValueType = 'primitive' | 'object' | 'array';
1201
1544
 
1202
1545
  /**
1203
1546
  * Helper to create a converter for a literal value.
1204
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
1547
+ * Accepts `IJsonConverterContext` but ignores it.
1205
1548
  * Mirrors the behavior of `@fgv/ts-utils`.
1206
1549
  * @public
1207
1550
  */
@@ -1209,26 +1552,32 @@ declare function literal<T>(value: T): Converter<T, IJsonConverterContext>;
1209
1552
 
1210
1553
  /**
1211
1554
  * Helper to create a validator for a literal value.
1212
- * Accepts {@link Validators.IJsonValidatorContext | IJsonValidatorContext} but ignores it.
1555
+ * Accepts `IJsonValidatorContext` but ignores it.
1213
1556
  * Mirrors the behavior of `@fgv/ts-utils`.
1214
1557
  * @public
1215
1558
  */
1216
1559
  declare function literal_2<T>(value: T): Validator<T, IJsonValidatorContext>;
1217
1560
 
1218
1561
  /**
1219
- * A {@link Converter | Converter} which converts `unknown` to a `number`.
1220
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
1562
+ * Type for a mutable item in a file tree.
1563
+ * @public
1564
+ */
1565
+ declare type MutableFileTreeItem<TCT extends string = string> = IMutableFileTreeFileItem<TCT> | IMutableFileTreeDirectoryItem<TCT>;
1566
+
1567
+ /**
1568
+ * A `Converter` which converts `unknown` to a `number`.
1569
+ * Accepts `IJsonConverterContext` but ignores it.
1221
1570
  * Mirrors the behavior of `@fgv/ts-utils`.
1222
1571
  * @public
1223
1572
  */
1224
1573
  declare const number: Converter<number, IJsonConverterContext>;
1225
1574
 
1226
1575
  /**
1227
- * A {@link Validation.Classes.NumberValidator | NumberValidator} which validates a number in place.
1576
+ * A `NumberValidator` which validates a number in place.
1228
1577
  * Accepts {@link Validators.IJsonValidatorContext | IJsonValidatorContext} but ignores it.
1229
1578
  * @public
1230
1579
  */
1231
- declare const number_2: Validator<number, IJsonValidatorContext>;
1580
+ declare const number_2: Validation.Classes.NumberValidator<number, IJsonValidatorContext>;
1232
1581
 
1233
1582
  /**
1234
1583
  * A helper function to create a {@link JsonCompatible.ObjectConverter | JSON-compatible ObjectConverter<T, TC>} which converts a
@@ -1285,7 +1634,7 @@ export declare function pickJsonObject(src: JsonObject, path: string): Result<Js
1285
1634
  export declare function pickJsonValue(src: JsonObject, path: string): Result<JsonValue>;
1286
1635
 
1287
1636
  /**
1288
- * {@inheritdoc JsonFile.JsonFsHelper.readJsonFileSync}
1637
+ * {@inheritDoc JsonFile.JsonFsHelper.readJsonFileSync}
1289
1638
  * @public
1290
1639
  */
1291
1640
  declare function readJsonFileSync(srcPath: string): Result<JsonValue>;
@@ -1351,6 +1700,31 @@ export declare function sanitizeJson(from: unknown): Result<JsonValue>;
1351
1700
  */
1352
1701
  export declare function sanitizeJsonObject<T>(from: T): Result<T>;
1353
1702
 
1703
+ /**
1704
+ * Indicates the persistence capability of a save operation.
1705
+ * - `persistent`: Changes are saved to durable storage (e.g., file system).
1706
+ * - `transient`: Changes are saved in memory only and will be lost on reload.
1707
+ * @public
1708
+ */
1709
+ declare type SaveCapability = 'persistent' | 'transient';
1710
+
1711
+ /**
1712
+ * Detail type for getIsMutable results.
1713
+ * @public
1714
+ */
1715
+ declare type SaveDetail = SaveCapability | SaveFailureReason;
1716
+
1717
+ /**
1718
+ * Indicates the reason a save operation cannot be performed.
1719
+ * - `not-supported`: The accessors do not support mutation.
1720
+ * - `read-only`: The file or file system is read-only.
1721
+ * - `not-mutable`: Mutability is disabled in configuration.
1722
+ * - `path-excluded`: The path is excluded by the mutability filter.
1723
+ * - `permission-denied`: Insufficient permissions to write.
1724
+ * @public
1725
+ */
1726
+ declare type SaveFailureReason = 'not-supported' | 'read-only' | 'not-mutable' | 'path-excluded' | 'permission-denied';
1727
+
1354
1728
  /**
1355
1729
  * A helper function to create a {@link JsonCompatible.ObjectConverter | JSON-compatible ObjectConverter<T, TC>} which converts a
1356
1730
  * supplied `unknown` value to a valid {@link JsonCompatibleType | JsonCompatibleType<T>} value.
@@ -1363,14 +1737,14 @@ export declare function sanitizeJsonObject<T>(from: T): Result<T>;
1363
1737
  declare function strictObject<T, TC = unknown>(properties: Conversion.FieldConverters<JsonCompatibleType<T>, TC>, options?: Converters_3.StrictObjectConverterOptions<JsonCompatibleType<T>>): JsonCompatible_2.ObjectConverter<T, TC>;
1364
1738
 
1365
1739
  /**
1366
- * A {@link Converter | Converter} which converts `unknown` to a `string`.
1367
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
1740
+ * A `StringConverter` which converts `unknown` to a `string`.
1741
+ * Accepts `IJsonConverterContext` but ignores it.
1368
1742
  * @public
1369
1743
  */
1370
1744
  declare const string: StringConverter<string, IJsonConverterContext>;
1371
1745
 
1372
1746
  /**
1373
- * A {@link Validation.Classes.StringValidator | StringValidator} which validates a string in place.
1747
+ * A `StringValidator` which validates a string in place.
1374
1748
  * Accepts {@link Validators.IJsonValidatorContext | IJsonValidatorContext} but ignores it.
1375
1749
  * @public
1376
1750
  */