@fgv/ts-json-base 5.0.2 → 5.1.0-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.
Files changed (42) hide show
  1. package/dist/packlets/converters/converters.js +36 -14
  2. package/dist/packlets/file-tree/directoryItem.js +35 -4
  3. package/dist/packlets/file-tree/fileItem.js +37 -9
  4. package/dist/packlets/file-tree/fileTreeAccessors.js +24 -1
  5. package/dist/packlets/file-tree/filterSpec.js +74 -0
  6. package/dist/packlets/file-tree/fsTree.js +73 -12
  7. package/dist/packlets/file-tree/in-memory/inMemoryTree.js +204 -21
  8. package/dist/packlets/file-tree/in-memory/treeBuilder.js +23 -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 +290 -61
  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 +13 -5
  19. package/lib/packlets/file-tree/directoryItem.js +34 -3
  20. package/lib/packlets/file-tree/fileItem.d.ts +26 -13
  21. package/lib/packlets/file-tree/fileItem.js +36 -8
  22. package/lib/packlets/file-tree/fileTreeAccessors.d.ts +141 -1
  23. package/lib/packlets/file-tree/fileTreeAccessors.js +26 -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 +29 -13
  27. package/lib/packlets/file-tree/fsTree.js +72 -11
  28. package/lib/packlets/file-tree/in-memory/inMemoryTree.d.ts +29 -13
  29. package/lib/packlets/file-tree/in-memory/inMemoryTree.js +203 -20
  30. package/lib/packlets/file-tree/in-memory/treeBuilder.d.ts +9 -0
  31. package/lib/packlets/file-tree/in-memory/treeBuilder.js +23 -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 +18 -18
@@ -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';
@@ -40,16 +41,16 @@ declare function arrayOf_2<T, TC = unknown>(validateElement: JsonCompatible_2.Va
40
41
  declare type ArrayValidator<T, TC = unknown> = Validation.Classes.ArrayValidator<JsonCompatibleType<T>, TC>;
41
42
 
42
43
  /**
43
- * A {@link Converter | Converter} which converts `unknown` to a `boolean`.
44
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
44
+ * A `Converter` which converts `unknown` to a `boolean`.
45
+ * Accepts `IJsonConverterContext` but ignores it.
45
46
  * Mirrors the behavior of `@fgv/ts-utils`.
46
47
  * @public
47
48
  */
48
49
  declare const boolean: Converter<boolean, IJsonConverterContext>;
49
50
 
50
51
  /**
51
- * A {@link Validation.Classes.BooleanValidator | BooleanValidator} which validates a boolean in place.
52
- * Accepts {@link Validators.IJsonValidatorContext | IJsonValidatorContext} but ignores it.
52
+ * A `BooleanValidator` which validates a boolean in place.
53
+ * Accepts `IJsonValidatorContext` but ignores it.
53
54
  * @public
54
55
  */
55
56
  declare const boolean_2: Validator<boolean, IJsonValidatorContext>;
@@ -80,6 +81,7 @@ declare namespace Converters {
80
81
  export {
81
82
  literal,
82
83
  enumeratedValue,
84
+ jsonConverter,
83
85
  IJsonConverterContext,
84
86
  jsonPrimitive,
85
87
  jsonObject,
@@ -158,15 +160,15 @@ declare const DefaultJsonTreeHelper: JsonTreeHelper;
158
160
  */
159
161
  declare class DirectoryItem<TCT extends string = string> implements IFileTreeDirectoryItem<TCT> {
160
162
  /**
161
- * {@inheritdoc FileTree.IFileTreeDirectoryItem."type"}
163
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem."type"}
162
164
  */
163
165
  readonly type: 'directory';
164
166
  /**
165
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.absolutePath}
167
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.absolutePath}
166
168
  */
167
169
  readonly absolutePath: string;
168
170
  /**
169
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.name}
171
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.name}
170
172
  */
171
173
  get name(): string;
172
174
  /**
@@ -192,9 +194,17 @@ declare class DirectoryItem<TCT extends string = string> implements IFileTreeDir
192
194
  */
193
195
  static create<TCT extends string = string>(path: string, hal: IFileTreeAccessors<TCT>): Result<DirectoryItem<TCT>>;
194
196
  /**
195
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.getChildren}
197
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.getChildren}
196
198
  */
197
199
  getChildren(): Result<ReadonlyArray<FileTreeItem<TCT>>>;
200
+ /**
201
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.createChildFile}
202
+ */
203
+ createChildFile(name: string, contents: string): Result<IFileTreeFileItem<TCT>>;
204
+ /**
205
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.createChildDirectory}
206
+ */
207
+ createChildDirectory(name: string): Result<IFileTreeDirectoryItem<TCT>>;
198
208
  }
199
209
 
200
210
  /**
@@ -209,33 +219,33 @@ declare class DirectoryItem<TCT extends string = string> implements IFileTreeDir
209
219
  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
220
 
211
221
  /**
212
- * Helper function to create a {@link Converter | Converter} which converts `unknown` to one of a set of
222
+ * Helper function to create a `Converter` which converts `unknown` to one of a set of
213
223
  * supplied enumerated values. Anything else fails.
214
224
  *
215
225
  * @remarks
216
- * This JSON variant accepts an {@link Converters.IJsonConverterContext | IJsonConverterContext} OR
226
+ * This JSON variant accepts an `IJsonConverterContext` OR
217
227
  * a `ReadonlyArray<T>` as its conversion context. If the context is an array, it is used to override the
218
228
  * allowed values for that conversion; otherwise, the original `values` supplied at creation time are used.
219
229
  *
220
230
  * @param values - Array of allowed values.
221
231
  * @param message - Optional custom failure message.
222
- * @returns A new {@link Converter | Converter} returning `<T>`.
232
+ * @returns A new `Converter` returning `<T>`.
223
233
  * @public
224
234
  */
225
235
  declare function enumeratedValue<T>(values: ReadonlyArray<T>, message?: string): Converter<T, IJsonConverterContext | ReadonlyArray<T>>;
226
236
 
227
237
  /**
228
- * Helper function to create a {@link Validator | Validator} which validates `unknown` to one of a set of
238
+ * Helper function to create a `Validator` which validates `unknown` to one of a set of
229
239
  * supplied enumerated values. Anything else fails.
230
240
  *
231
241
  * @remarks
232
- * This JSON variant accepts an {@link Validators.IJsonValidatorContext | IJsonValidatorContext} OR
242
+ * This JSON variant accepts an `IJsonValidatorContext` OR
233
243
  * a `ReadonlyArray<T>` as its validation context. If the context is an array, it is used to override the
234
244
  * allowed values for that validation; otherwise, the original `values` supplied at creation time are used.
235
245
  *
236
246
  * @param values - Array of allowed values.
237
247
  * @param message - Optional custom failure message.
238
- * @returns A new {@link Validator | Validator} returning `<T>`.
248
+ * @returns A new `Validator` returning `<T>`.
239
249
  * @public
240
250
  */
241
251
  declare function enumeratedValue_2<T>(values: ReadonlyArray<T>, message?: string): Validator<T, IJsonValidatorContext | ReadonlyArray<T>>;
@@ -246,27 +256,27 @@ declare function enumeratedValue_2<T>(values: ReadonlyArray<T>, message?: string
246
256
  */
247
257
  declare class FileItem<TCT extends string = string> implements IFileTreeFileItem<TCT> {
248
258
  /**
249
- * {@inheritdoc FileTree.IFileTreeFileItem."type"}
259
+ * {@inheritDoc FileTree.IFileTreeFileItem."type"}
250
260
  */
251
261
  readonly type: 'file';
252
262
  /**
253
- * {@inheritdoc FileTree.IFileTreeFileItem.absolutePath}
263
+ * {@inheritDoc FileTree.IFileTreeFileItem.absolutePath}
254
264
  */
255
265
  readonly absolutePath: string;
256
266
  /**
257
- * {@inheritdoc FileTree.IFileTreeFileItem.name}
267
+ * {@inheritDoc FileTree.IFileTreeFileItem.name}
258
268
  */
259
269
  get name(): string;
260
270
  /**
261
- * {@inheritdoc FileTree.IFileTreeFileItem.baseName}
271
+ * {@inheritDoc FileTree.IFileTreeFileItem.baseName}
262
272
  */
263
273
  get baseName(): string;
264
274
  /**
265
- * {@inheritdoc FileTree.IFileTreeFileItem.extension}
275
+ * {@inheritDoc FileTree.IFileTreeFileItem.extension}
266
276
  */
267
277
  get extension(): string;
268
278
  /**
269
- * {@inheritdoc FileTree.IFileTreeFileItem.contentType}
279
+ * {@inheritDoc FileTree.IFileTreeFileItem.contentType}
270
280
  */
271
281
  get contentType(): TCT | undefined;
272
282
  /**
@@ -296,15 +306,19 @@ declare class FileItem<TCT extends string = string> implements IFileTreeFileItem
296
306
  */
297
307
  static create<TCT extends string = string>(path: string, hal: IFileTreeAccessors<TCT>): Result<FileItem<TCT>>;
298
308
  /**
299
- * {@inheritdoc FileTree.IFileTreeFileItem.(getContents:1)}
309
+ * {@inheritDoc FileTree.IFileTreeFileItem.getIsMutable}
310
+ */
311
+ getIsMutable(): DetailedResult<boolean, SaveDetail>;
312
+ /**
313
+ * {@inheritDoc FileTree.IFileTreeFileItem.getContents}
300
314
  */
301
315
  getContents(): Result<JsonValue>;
302
316
  /**
303
- * {@inheritdoc FileTree.IFileTreeFileItem.(getContents:2)}
317
+ * {@inheritDoc FileTree.IFileTreeFileItem.getContents}
304
318
  */
305
319
  getContents<T>(converter: Validator<T> | Converter<T>): Result<T>;
306
320
  /**
307
- * {@inheritdoc FileTree.IFileTreeFileItem.getRawContents}
321
+ * {@inheritDoc FileTree.IFileTreeFileItem.getRawContents}
308
322
  */
309
323
  getRawContents(): Result<string>;
310
324
  /**
@@ -312,15 +326,24 @@ declare class FileItem<TCT extends string = string> implements IFileTreeFileItem
312
326
  * @param contentType - The content type of the file.
313
327
  */
314
328
  setContentType(contentType: TCT | undefined): void;
329
+ /**
330
+ * {@inheritDoc FileTree.IFileTreeFileItem.setContents}
331
+ */
332
+ setContents(json: JsonValue): Result<JsonValue>;
333
+ /**
334
+ * {@inheritDoc FileTree.IFileTreeFileItem.setRawContents}
335
+ */
336
+ setRawContents(contents: string): Result<string>;
315
337
  /**
316
338
  * Default function to infer the content type of a file.
317
339
  * @param filePath - The path of the file.
340
+ * @param provided - Optional supplied content type.
318
341
  * @returns `Success` with the content type of the file if successful, or
319
342
  * `Failure` with an error message otherwise.
320
343
  * @remarks This default implementation always returns `Success` with `undefined`.
321
344
  * @public
322
345
  */
323
- static defaultInferContentType<TCT extends string = string>(__filePath: string, __provided?: string): Result<TCT | undefined>;
346
+ static defaultInferContentType<TCT extends string = string>(filePath: string, provided?: string): Result<TCT | undefined>;
324
347
  /**
325
348
  * Default function to accept the content type of a file.
326
349
  * @param filePath - The path of the file.
@@ -330,12 +353,18 @@ declare class FileItem<TCT extends string = string> implements IFileTreeFileItem
330
353
  * @remarks This default implementation always returns `Success` with `undefined`.
331
354
  * @public
332
355
  */
333
- static defaultAcceptContentType<TCT extends string = string>(__filePath: string, provided?: TCT): Result<TCT | undefined>;
356
+ static defaultAcceptContentType<TCT extends string = string>(filePath: string, provided?: TCT): Result<TCT | undefined>;
334
357
  }
335
358
 
336
359
  declare namespace FileTree {
337
360
  export {
338
361
  inMemory,
362
+ isMutableAccessors,
363
+ isPersistentAccessors,
364
+ SaveCapability,
365
+ SaveFailureReason,
366
+ SaveDetail,
367
+ IFilterSpec,
339
368
  FileTreeItemType,
340
369
  ContentTypeFactory,
341
370
  IFileTreeInitParams,
@@ -343,9 +372,12 @@ declare namespace FileTree {
343
372
  IFileTreeDirectoryItem,
344
373
  FileTreeItem,
345
374
  IFileTreeAccessors,
375
+ IMutableFileTreeAccessors,
376
+ IPersistentFileTreeAccessors,
346
377
  FileTree_2 as FileTree,
347
378
  DirectoryItem,
348
379
  FileItem,
380
+ isPathMutable,
349
381
  forFilesystem,
350
382
  IInMemoryFile,
351
383
  InMemoryTreeAccessors,
@@ -434,11 +466,11 @@ declare function forFilesystem<TCT extends string = string>(prefix?: string): Re
434
466
  declare function forFilesystem<TCT extends string = string>(params?: IFileTreeInitParams<TCT>): Result<FileTree_2<TCT>>;
435
467
 
436
468
  /**
437
- * Implementation of {@link FileTree.IFileTreeAccessors} that uses the
438
- * file system to access files and directories.
469
+ * Implementation of {@link FileTree.IMutableFileTreeAccessors} that uses the
470
+ * file system to access and modify files and directories.
439
471
  * @public
440
472
  */
441
- declare class FsFileTreeAccessors<TCT extends string = string> implements IFileTreeAccessors<TCT> {
473
+ declare class FsFileTreeAccessors<TCT extends string = string> implements IMutableFileTreeAccessors<TCT> {
442
474
  /**
443
475
  * Optional path prefix to prepend to all paths.
444
476
  */
@@ -448,6 +480,10 @@ declare class FsFileTreeAccessors<TCT extends string = string> implements IFileT
448
480
  * @public
449
481
  */
450
482
  protected readonly _inferContentType: (filePath: string) => Result<TCT | undefined>;
483
+ /**
484
+ * The mutability configuration.
485
+ */
486
+ private readonly _mutable;
451
487
  /**
452
488
  * Construct a new instance of the {@link FileTree.FsFileTreeAccessors | FsFileTreeAccessors} class.
453
489
  * @param params - Optional {@link FileTree.IFileTreeInitParams | initialization parameters}.
@@ -455,37 +491,49 @@ declare class FsFileTreeAccessors<TCT extends string = string> implements IFileT
455
491
  */
456
492
  constructor(params?: IFileTreeInitParams<TCT>);
457
493
  /**
458
- * {@inheritdoc FileTree.IFileTreeAccessors.resolveAbsolutePath}
494
+ * {@inheritDoc FileTree.IFileTreeAccessors.resolveAbsolutePath}
459
495
  */
460
496
  resolveAbsolutePath(...paths: string[]): string;
461
497
  /**
462
- * {@inheritdoc FileTree.IFileTreeAccessors.getExtension}
498
+ * {@inheritDoc FileTree.IFileTreeAccessors.getExtension}
463
499
  */
464
500
  getExtension(itemPath: string): string;
465
501
  /**
466
- * {@inheritdoc FileTree.IFileTreeAccessors.getBaseName}
502
+ * {@inheritDoc FileTree.IFileTreeAccessors.getBaseName}
467
503
  */
468
504
  getBaseName(itemPath: string, suffix?: string): string;
469
505
  /**
470
- * {@inheritdoc FileTree.IFileTreeAccessors.joinPaths}
506
+ * {@inheritDoc FileTree.IFileTreeAccessors.joinPaths}
471
507
  */
472
508
  joinPaths(...paths: string[]): string;
473
509
  /**
474
- * {@inheritdoc FileTree.IFileTreeAccessors.getItem}
510
+ * {@inheritDoc FileTree.IFileTreeAccessors.getItem}
475
511
  */
476
512
  getItem(itemPath: string): Result<FileTreeItem<TCT>>;
477
513
  /**
478
- * {@inheritdoc FileTree.IFileTreeAccessors.getFileContents}
514
+ * {@inheritDoc FileTree.IFileTreeAccessors.getFileContents}
479
515
  */
480
516
  getFileContents(filePath: string): Result<string>;
481
517
  /**
482
- * {@inheritdoc FileTree.IFileTreeAccessors.getFileContentType}
518
+ * {@inheritDoc FileTree.IFileTreeAccessors.getFileContentType}
483
519
  */
484
520
  getFileContentType(filePath: string, provided?: string): Result<TCT | undefined>;
485
521
  /**
486
- * {@inheritdoc FileTree.IFileTreeAccessors.getChildren}
522
+ * {@inheritDoc FileTree.IFileTreeAccessors.getChildren}
487
523
  */
488
524
  getChildren(dirPath: string): Result<ReadonlyArray<FileTreeItem<TCT>>>;
525
+ /**
526
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.fileIsMutable}
527
+ */
528
+ fileIsMutable(path: string): DetailedResult<boolean, SaveDetail>;
529
+ /**
530
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.saveFileContents}
531
+ */
532
+ saveFileContents(path: string, contents: string): Result<string>;
533
+ /**
534
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.createDirectory}
535
+ */
536
+ createDirectory(dirPath: string): Result<string>;
489
537
  }
490
538
 
491
539
  /**
@@ -569,6 +617,21 @@ declare interface IFileTreeDirectoryItem<TCT extends string = string> {
569
617
  * or `Failure` with an error message otherwise.
570
618
  */
571
619
  getChildren(): Result<ReadonlyArray<FileTreeItem<TCT>>>;
620
+ /**
621
+ * Creates a new file as a child of this directory.
622
+ * @param name - The file name to create.
623
+ * @param contents - The string contents to write.
624
+ * @returns `Success` with the new file item, or `Failure` with an error message.
625
+ * @remarks This method is optional. Only available on mutable directory items.
626
+ */
627
+ createChildFile?(name: string, contents: string): Result<IFileTreeFileItem<TCT>>;
628
+ /**
629
+ * Creates a new subdirectory as a child of this directory.
630
+ * @param name - The directory name to create.
631
+ * @returns `Success` with the new directory item, or `Failure` with an error message.
632
+ * @remarks This method is optional. Only available on mutable directory items.
633
+ */
634
+ createChildDirectory?(name: string): Result<IFileTreeDirectoryItem<TCT>>;
572
635
  }
573
636
 
574
637
  /**
@@ -620,6 +683,27 @@ declare interface IFileTreeFileItem<TCT extends string = string> {
620
683
  * `Failure` with an error message otherwise.
621
684
  */
622
685
  getRawContents(): Result<string>;
686
+ /**
687
+ * Indicates whether this file can be saved.
688
+ * @returns `DetailedSuccess` with {@link FileTree.SaveCapability} if the file can be saved,
689
+ * or `DetailedFailure` with {@link FileTree.SaveFailureReason} if it cannot.
690
+ * @remarks This property is optional. If not present, the file is not mutable.
691
+ */
692
+ getIsMutable(): DetailedResult<boolean, SaveDetail>;
693
+ /**
694
+ * Sets the contents of the file from a JSON value.
695
+ * @param json - The JSON value to serialize and save.
696
+ * @returns `Success` if the file was saved, or `Failure` with an error message.
697
+ * @remarks This method is optional. If not present, the file is not mutable.
698
+ */
699
+ setContents(json: JsonValue): Result<JsonValue>;
700
+ /**
701
+ * Sets the raw contents of the file.
702
+ * @param contents - The string contents to save.
703
+ * @returns `Success` if the file was saved, or `Failure` with an error message.
704
+ * @remarks This method is optional. If not present, the file is not mutable.
705
+ */
706
+ setRawContents(contents: string): Result<string>;
623
707
  }
624
708
 
625
709
  /**
@@ -629,6 +713,28 @@ declare interface IFileTreeFileItem<TCT extends string = string> {
629
713
  declare interface IFileTreeInitParams<TCT extends string = string> {
630
714
  prefix?: string;
631
715
  inferContentType?: ContentTypeFactory<TCT>;
716
+ /**
717
+ * Controls mutability of the file tree.
718
+ * - `undefined` or `false`: No files are mutable.
719
+ * - `true`: All files are mutable.
720
+ * - `IFilterSpec`: Only files matching the filter are mutable.
721
+ */
722
+ mutable?: boolean | IFilterSpec;
723
+ }
724
+
725
+ /**
726
+ * Filter specification for controlling which paths are mutable.
727
+ * @public
728
+ */
729
+ declare interface IFilterSpec {
730
+ /**
731
+ * Paths or patterns to include. If specified, only matching paths are mutable.
732
+ */
733
+ include?: (string | RegExp)[];
734
+ /**
735
+ * Paths or patterns to exclude. Matching paths are not mutable.
736
+ */
737
+ exclude?: (string | RegExp)[];
632
738
  }
633
739
 
634
740
  /**
@@ -708,6 +814,33 @@ declare interface IJsonValidatorContext {
708
814
  ignoreUndefinedProperties?: boolean;
709
815
  }
710
816
 
817
+ /**
818
+ * Extended accessors interface that supports mutation operations.
819
+ * @public
820
+ */
821
+ declare interface IMutableFileTreeAccessors<TCT extends string = string> extends IFileTreeAccessors<TCT> {
822
+ /**
823
+ * Checks if a file at the given path can be saved.
824
+ * @param path - The path to check.
825
+ * @returns `DetailedSuccess` with {@link FileTree.SaveCapability} if the file can be saved,
826
+ * or `DetailedFailure` with {@link FileTree.SaveFailureReason} if it cannot.
827
+ */
828
+ fileIsMutable(path: string): DetailedResult<boolean, SaveDetail>;
829
+ /**
830
+ * Saves the contents to a file at the given path.
831
+ * @param path - The path of the file to save.
832
+ * @param contents - The string contents to save.
833
+ * @returns `Success` if the file was saved, or `Failure` with an error message.
834
+ */
835
+ saveFileContents(path: string, contents: string): Result<string>;
836
+ /**
837
+ * Creates a directory at the given path, including any missing parent directories.
838
+ * @param path - The path of the directory to create.
839
+ * @returns `Success` with the absolute path if created, or `Failure` with an error message.
840
+ */
841
+ createDirectory?(path: string): Result<string>;
842
+ }
843
+
711
844
  /**
712
845
  * Helper function to create a new {@link FileTree.FileTree | FileTree} instance
713
846
  * with accessors for an in-memory file tree.
@@ -731,13 +864,16 @@ declare function inMemory<TCT extends string = string>(files: IInMemoryFile<TCT>
731
864
  declare function inMemory<TCT extends string = string>(files: IInMemoryFile<TCT>[], params?: IFileTreeInitParams<TCT>): Result<FileTree_2<TCT>>;
732
865
 
733
866
  /**
734
- * Implementation of {@link FileTree.IFileTreeAccessors} that uses an in-memory
735
- * tree to access files and directories.
867
+ * Implementation of {@link FileTree.IMutableFileTreeAccessors} that uses an in-memory
868
+ * tree to access and modify files and directories.
736
869
  * @public
737
870
  */
738
- declare class InMemoryTreeAccessors<TCT extends string = string> implements IFileTreeAccessors<TCT> {
871
+ declare class InMemoryTreeAccessors<TCT extends string = string> implements IMutableFileTreeAccessors<TCT> {
739
872
  private readonly _tree;
740
873
  private readonly _inferContentType;
874
+ private readonly _mutable;
875
+ private readonly _mutableByPath;
876
+ private readonly _mutableRoot;
741
877
  /**
742
878
  * Protected constructor for derived classes.
743
879
  * @param files - An array of {@link FileTree.IInMemoryFile | in-memory files} to include in the tree.
@@ -760,37 +896,72 @@ declare class InMemoryTreeAccessors<TCT extends string = string> implements IFil
760
896
  */
761
897
  static create<TCT extends string = string>(files: IInMemoryFile<TCT>[], params?: IFileTreeInitParams<TCT>): Result<InMemoryTreeAccessors<TCT>>;
762
898
  /**
763
- * {@inheritdoc FileTree.IFileTreeAccessors.resolveAbsolutePath}
899
+ * {@inheritDoc FileTree.IFileTreeAccessors.resolveAbsolutePath}
764
900
  */
765
901
  resolveAbsolutePath(...paths: string[]): string;
766
902
  /**
767
- * {@inheritdoc FileTree.IFileTreeAccessors.getExtension}
903
+ * {@inheritDoc FileTree.IFileTreeAccessors.getExtension}
768
904
  */
769
905
  getExtension(path: string): string;
770
906
  /**
771
- * {@inheritdoc FileTree.IFileTreeAccessors.getBaseName}
907
+ * {@inheritDoc FileTree.IFileTreeAccessors.getBaseName}
772
908
  */
773
909
  getBaseName(path: string, suffix?: string): string;
774
910
  /**
775
- * {@inheritdoc FileTree.IFileTreeAccessors.joinPaths}
911
+ * {@inheritDoc FileTree.IFileTreeAccessors.joinPaths}
776
912
  */
777
913
  joinPaths(...paths: string[]): string;
778
914
  /**
779
- * {@inheritdoc FileTree.IFileTreeAccessors.getItem}
915
+ * {@inheritDoc FileTree.IFileTreeAccessors.getItem}
780
916
  */
781
917
  getItem(itemPath: string): Result<FileTreeItem<TCT>>;
782
918
  /**
783
- * {@inheritdoc FileTree.IFileTreeAccessors.getFileContents}
919
+ * {@inheritDoc FileTree.IFileTreeAccessors.getFileContents}
784
920
  */
785
921
  getFileContents(path: string): Result<string>;
786
922
  /**
787
- * {@inheritdoc FileTree.IFileTreeAccessors.getFileContentType}
923
+ * {@inheritDoc FileTree.IFileTreeAccessors.getFileContentType}
788
924
  */
789
925
  getFileContentType(path: string, provided?: string): Result<TCT | undefined>;
790
926
  /**
791
- * {@inheritdoc FileTree.IFileTreeAccessors.getChildren}
927
+ * {@inheritDoc FileTree.IFileTreeAccessors.getChildren}
792
928
  */
793
929
  getChildren(path: string): Result<ReadonlyArray<FileTreeItem<TCT>>>;
930
+ private _addMutableFile;
931
+ /**
932
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.createDirectory}
933
+ */
934
+ createDirectory(dirPath: string): Result<string>;
935
+ /**
936
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.fileIsMutable}
937
+ */
938
+ fileIsMutable(path: string): DetailedResult<boolean, SaveDetail>;
939
+ /**
940
+ * {@inheritDoc FileTree.IMutableFileTreeAccessors.saveFileContents}
941
+ */
942
+ saveFileContents(path: string, contents: string): Result<string>;
943
+ }
944
+
945
+ /**
946
+ * Extended accessors interface that supports persistence operations.
947
+ * @public
948
+ */
949
+ declare interface IPersistentFileTreeAccessors<TCT extends string = string> extends IMutableFileTreeAccessors<TCT> {
950
+ /**
951
+ * Synchronize all dirty files to persistent storage.
952
+ * @returns Promise resolving to success or failure
953
+ */
954
+ syncToDisk(): Promise<Result<void>>;
955
+ /**
956
+ * Check if there are unsaved changes.
957
+ * @returns True if there are dirty files
958
+ */
959
+ isDirty(): boolean;
960
+ /**
961
+ * Get paths of all files with unsaved changes.
962
+ * @returns Array of dirty file paths
963
+ */
964
+ getDirtyPaths(): string[];
794
965
  }
795
966
 
796
967
  /**
@@ -835,6 +1006,31 @@ export declare function isJsonObject(from: unknown): from is JsonObject;
835
1006
  */
836
1007
  export declare function isJsonPrimitive(from: unknown): from is JsonPrimitive;
837
1008
 
1009
+ /**
1010
+ * Type guard to check if accessors support mutation.
1011
+ * @param accessors - The accessors to check.
1012
+ * @returns `true` if the accessors implement {@link FileTree.IMutableFileTreeAccessors}.
1013
+ * @public
1014
+ */
1015
+ declare function isMutableAccessors<TCT extends string = string>(accessors: IFileTreeAccessors<TCT>): accessors is IMutableFileTreeAccessors<TCT>;
1016
+
1017
+ /**
1018
+ * Checks if a path is allowed by a mutability configuration.
1019
+ * @param path - The path to check.
1020
+ * @param mutable - The mutability configuration.
1021
+ * @returns `true` if the path is mutable according to the configuration.
1022
+ * @public
1023
+ */
1024
+ declare function isPathMutable(path: string, mutable: boolean | IFilterSpec | undefined): boolean;
1025
+
1026
+ /**
1027
+ * Type guard to check if accessors support persistence.
1028
+ * @param accessors - The accessors to check.
1029
+ * @returns `true` if the accessors implement {@link FileTree.IPersistentFileTreeAccessors}.
1030
+ * @public
1031
+ */
1032
+ declare function isPersistentAccessors<TCT extends string = string>(accessors: IFileTreeAccessors<TCT>): accessors is IPersistentFileTreeAccessors<TCT>;
1033
+
838
1034
  /**
839
1035
  * Helper type to detect if T is exactly unknown.
840
1036
  */
@@ -858,7 +1054,7 @@ export declare interface JsonArray extends Array<JsonValue> {
858
1054
  * An copying converter which converts a supplied `unknown` value to
859
1055
  * a valid {@link JsonArray | JsonArray}. Fails by default if any properties or array elements
860
1056
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
861
- * {@link Converters.IJsonConverterContext | context} at runtime.
1057
+ * `IJsonConverterContext` at runtime.
862
1058
  *
863
1059
  * Guaranteed to return a new array.
864
1060
  * @public
@@ -954,6 +1150,14 @@ export declare type JsonCompatibleType<T> = IsUnknown<T> extends true ? JsonValu
954
1150
  [K in keyof T]: JsonCompatibleType<T[K]>;
955
1151
  } : ['Error: Non-JSON type'];
956
1152
 
1153
+ /**
1154
+ * Creates a converter that parses JSON string content and then applies the supplied converter.
1155
+ * @param converter - Converter to apply to the parsed JSON
1156
+ * @returns Converter that parses JSON then validates
1157
+ * @public
1158
+ */
1159
+ declare function jsonConverter<T>(converter: Converter<T>): Converter<T>;
1160
+
957
1161
  declare namespace JsonFile {
958
1162
  export {
959
1163
  readJsonFileSync,
@@ -994,7 +1198,7 @@ declare class JsonFsHelper {
994
1198
  readonly config: IJsonFsHelperConfig;
995
1199
  /**
996
1200
  * Construct a new {@link JsonFile.JsonFsHelper | JsonFsHelper}.
997
- * @param json - Optional {@link JsonFile.IJsonLike | IJsonLike} used to process strings
1201
+ * @param init - Optional {@link JsonFile.JsonFsHelperInitOptions | init options} to construct
998
1202
  * and JSON values.
999
1203
  */
1000
1204
  constructor(init?: JsonFsHelperInitOptions);
@@ -1057,7 +1261,7 @@ export declare interface JsonObject {
1057
1261
  * An copying converter which converts a supplied `unknown` value into
1058
1262
  * a valid {@link JsonObject | JsonObject}. Fails by default if any properties or array elements
1059
1263
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
1060
- * {@link Converters.IJsonConverterContext | context} at runtime.
1264
+ * `IJsonConverterContext` at runtime.
1061
1265
  *
1062
1266
  * Guaranteed to return a new object.
1063
1267
  * @public
@@ -1179,7 +1383,7 @@ export declare type JsonValue = JsonPrimitive | JsonObject | JsonArray;
1179
1383
  * An copying converter which converts a supplied `unknown` value to a
1180
1384
  * valid {@link JsonValue | JsonValue}. Fails by default if any properties or array elements
1181
1385
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
1182
- * {@link Converters.IJsonConverterContext | context} at runtime.
1386
+ * `IJsonConverterContext` at runtime.
1183
1387
  * @public
1184
1388
  */
1185
1389
  declare const jsonValue: Converter<JsonValue, IJsonConverterContext>;
@@ -1201,7 +1405,7 @@ export declare type JsonValueType = 'primitive' | 'object' | 'array';
1201
1405
 
1202
1406
  /**
1203
1407
  * Helper to create a converter for a literal value.
1204
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
1408
+ * Accepts `IJsonConverterContext` but ignores it.
1205
1409
  * Mirrors the behavior of `@fgv/ts-utils`.
1206
1410
  * @public
1207
1411
  */
@@ -1209,26 +1413,26 @@ declare function literal<T>(value: T): Converter<T, IJsonConverterContext>;
1209
1413
 
1210
1414
  /**
1211
1415
  * Helper to create a validator for a literal value.
1212
- * Accepts {@link Validators.IJsonValidatorContext | IJsonValidatorContext} but ignores it.
1416
+ * Accepts `IJsonValidatorContext` but ignores it.
1213
1417
  * Mirrors the behavior of `@fgv/ts-utils`.
1214
1418
  * @public
1215
1419
  */
1216
1420
  declare function literal_2<T>(value: T): Validator<T, IJsonValidatorContext>;
1217
1421
 
1218
1422
  /**
1219
- * A {@link Converter | Converter} which converts `unknown` to a `number`.
1220
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
1423
+ * A `Converter` which converts `unknown` to a `number`.
1424
+ * Accepts `IJsonConverterContext` but ignores it.
1221
1425
  * Mirrors the behavior of `@fgv/ts-utils`.
1222
1426
  * @public
1223
1427
  */
1224
1428
  declare const number: Converter<number, IJsonConverterContext>;
1225
1429
 
1226
1430
  /**
1227
- * A {@link Validation.Classes.NumberValidator | NumberValidator} which validates a number in place.
1431
+ * A `NumberValidator` which validates a number in place.
1228
1432
  * Accepts {@link Validators.IJsonValidatorContext | IJsonValidatorContext} but ignores it.
1229
1433
  * @public
1230
1434
  */
1231
- declare const number_2: Validator<number, IJsonValidatorContext>;
1435
+ declare const number_2: Validation.Classes.NumberValidator<number, IJsonValidatorContext>;
1232
1436
 
1233
1437
  /**
1234
1438
  * A helper function to create a {@link JsonCompatible.ObjectConverter | JSON-compatible ObjectConverter<T, TC>} which converts a
@@ -1285,7 +1489,7 @@ export declare function pickJsonObject(src: JsonObject, path: string): Result<Js
1285
1489
  export declare function pickJsonValue(src: JsonObject, path: string): Result<JsonValue>;
1286
1490
 
1287
1491
  /**
1288
- * {@inheritdoc JsonFile.JsonFsHelper.readJsonFileSync}
1492
+ * {@inheritDoc JsonFile.JsonFsHelper.readJsonFileSync}
1289
1493
  * @public
1290
1494
  */
1291
1495
  declare function readJsonFileSync(srcPath: string): Result<JsonValue>;
@@ -1351,6 +1555,31 @@ export declare function sanitizeJson(from: unknown): Result<JsonValue>;
1351
1555
  */
1352
1556
  export declare function sanitizeJsonObject<T>(from: T): Result<T>;
1353
1557
 
1558
+ /**
1559
+ * Indicates the persistence capability of a save operation.
1560
+ * - `persistent`: Changes are saved to durable storage (e.g., file system).
1561
+ * - `transient`: Changes are saved in memory only and will be lost on reload.
1562
+ * @public
1563
+ */
1564
+ declare type SaveCapability = 'persistent' | 'transient';
1565
+
1566
+ /**
1567
+ * Detail type for getIsMutable results.
1568
+ * @public
1569
+ */
1570
+ declare type SaveDetail = SaveCapability | SaveFailureReason;
1571
+
1572
+ /**
1573
+ * Indicates the reason a save operation cannot be performed.
1574
+ * - `not-supported`: The accessors do not support mutation.
1575
+ * - `read-only`: The file or file system is read-only.
1576
+ * - `not-mutable`: Mutability is disabled in configuration.
1577
+ * - `path-excluded`: The path is excluded by the mutability filter.
1578
+ * - `permission-denied`: Insufficient permissions to write.
1579
+ * @public
1580
+ */
1581
+ declare type SaveFailureReason = 'not-supported' | 'read-only' | 'not-mutable' | 'path-excluded' | 'permission-denied';
1582
+
1354
1583
  /**
1355
1584
  * A helper function to create a {@link JsonCompatible.ObjectConverter | JSON-compatible ObjectConverter<T, TC>} which converts a
1356
1585
  * supplied `unknown` value to a valid {@link JsonCompatibleType | JsonCompatibleType<T>} value.
@@ -1363,14 +1592,14 @@ export declare function sanitizeJsonObject<T>(from: T): Result<T>;
1363
1592
  declare function strictObject<T, TC = unknown>(properties: Conversion.FieldConverters<JsonCompatibleType<T>, TC>, options?: Converters_3.StrictObjectConverterOptions<JsonCompatibleType<T>>): JsonCompatible_2.ObjectConverter<T, TC>;
1364
1593
 
1365
1594
  /**
1366
- * A {@link Converter | Converter} which converts `unknown` to a `string`.
1367
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
1595
+ * A `StringConverter` which converts `unknown` to a `string`.
1596
+ * Accepts `IJsonConverterContext` but ignores it.
1368
1597
  * @public
1369
1598
  */
1370
1599
  declare const string: StringConverter<string, IJsonConverterContext>;
1371
1600
 
1372
1601
  /**
1373
- * A {@link Validation.Classes.StringValidator | StringValidator} which validates a string in place.
1602
+ * A `StringValidator` which validates a string in place.
1374
1603
  * Accepts {@link Validators.IJsonValidatorContext | IJsonValidatorContext} but ignores it.
1375
1604
  * @public
1376
1605
  */