@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
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.54.0"
8
+ "packageVersion": "7.57.6"
9
9
  }
10
10
  ]
11
11
  }
@@ -16,7 +16,7 @@ export declare const jsonPrimitive: Converter<JsonPrimitive, IJsonConverterConte
16
16
  * An copying converter which converts a supplied `unknown` value into
17
17
  * a valid {@link JsonObject | JsonObject}. Fails by default if any properties or array elements
18
18
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
19
- * {@link Converters.IJsonConverterContext | context} at runtime.
19
+ * `IJsonConverterContext` at runtime.
20
20
  *
21
21
  * Guaranteed to return a new object.
22
22
  * @public
@@ -26,7 +26,7 @@ export declare const jsonObject: Converter<JsonObject, IJsonConverterContext>;
26
26
  * An copying converter which converts a supplied `unknown` value to
27
27
  * a valid {@link JsonArray | JsonArray}. Fails by default if any properties or array elements
28
28
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
29
- * {@link Converters.IJsonConverterContext | context} at runtime.
29
+ * `IJsonConverterContext` at runtime.
30
30
  *
31
31
  * Guaranteed to return a new array.
32
32
  * @public
@@ -36,50 +36,57 @@ export declare const jsonArray: Converter<JsonArray, IJsonConverterContext>;
36
36
  * An copying converter which converts a supplied `unknown` value to a
37
37
  * valid {@link JsonValue | JsonValue}. Fails by default if any properties or array elements
38
38
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
39
- * {@link Converters.IJsonConverterContext | context} at runtime.
39
+ * `IJsonConverterContext` at runtime.
40
40
  * @public
41
41
  */
42
42
  export declare const jsonValue: Converter<JsonValue, IJsonConverterContext>;
43
43
  /**
44
- * A {@link Converter | Converter} which converts `unknown` to a `string`.
45
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
44
+ * A `StringConverter` which converts `unknown` to a `string`.
45
+ * Accepts `IJsonConverterContext` but ignores it.
46
46
  * @public
47
47
  */
48
48
  export declare const string: StringConverter<string, IJsonConverterContext>;
49
49
  /**
50
- * A {@link Converter | Converter} which converts `unknown` to a `number`.
51
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
50
+ * A `Converter` which converts `unknown` to a `number`.
51
+ * Accepts `IJsonConverterContext` but ignores it.
52
52
  * Mirrors the behavior of `@fgv/ts-utils`.
53
53
  * @public
54
54
  */
55
55
  export declare const number: Converter<number, IJsonConverterContext>;
56
56
  /**
57
- * A {@link Converter | Converter} which converts `unknown` to a `boolean`.
58
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
57
+ * A `Converter` which converts `unknown` to a `boolean`.
58
+ * Accepts `IJsonConverterContext` but ignores it.
59
59
  * Mirrors the behavior of `@fgv/ts-utils`.
60
60
  * @public
61
61
  */
62
62
  export declare const boolean: Converter<boolean, IJsonConverterContext>;
63
63
  /**
64
64
  * Helper to create a converter for a literal value.
65
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
65
+ * Accepts `IJsonConverterContext` but ignores it.
66
66
  * Mirrors the behavior of `@fgv/ts-utils`.
67
67
  * @public
68
68
  */
69
69
  export declare function literal<T>(value: T): Converter<T, IJsonConverterContext>;
70
70
  /**
71
- * Helper function to create a {@link Converter | Converter} which converts `unknown` to one of a set of
71
+ * Helper function to create a `Converter` which converts `unknown` to one of a set of
72
72
  * supplied enumerated values. Anything else fails.
73
73
  *
74
74
  * @remarks
75
- * This JSON variant accepts an {@link Converters.IJsonConverterContext | IJsonConverterContext} OR
75
+ * This JSON variant accepts an `IJsonConverterContext` OR
76
76
  * a `ReadonlyArray<T>` as its conversion context. If the context is an array, it is used to override the
77
77
  * allowed values for that conversion; otherwise, the original `values` supplied at creation time are used.
78
78
  *
79
79
  * @param values - Array of allowed values.
80
80
  * @param message - Optional custom failure message.
81
- * @returns A new {@link Converter | Converter} returning `<T>`.
81
+ * @returns A new `Converter` returning `<T>`.
82
82
  * @public
83
83
  */
84
84
  export declare function enumeratedValue<T>(values: ReadonlyArray<T>, message?: string): Converter<T, IJsonConverterContext | ReadonlyArray<T>>;
85
+ /**
86
+ * Creates a converter that parses JSON string content and then applies the supplied converter.
87
+ * @param converter - Converter to apply to the parsed JSON
88
+ * @returns Converter that parses JSON then validates
89
+ * @public
90
+ */
91
+ export declare function jsonConverter<T>(converter: Converter<T>): Converter<T>;
85
92
  //# sourceMappingURL=converters.d.ts.map
@@ -24,6 +24,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.boolean = exports.number = exports.string = exports.jsonValue = exports.jsonArray = exports.jsonObject = exports.jsonPrimitive = void 0;
25
25
  exports.literal = literal;
26
26
  exports.enumeratedValue = enumeratedValue;
27
+ exports.jsonConverter = jsonConverter;
27
28
  const ts_utils_1 = require("@fgv/ts-utils");
28
29
  const json_1 = require("../json");
29
30
  /**
@@ -50,7 +51,7 @@ exports.jsonPrimitive = new ts_utils_1.Conversion.BaseConverter((from, __self, c
50
51
  * An copying converter which converts a supplied `unknown` value into
51
52
  * a valid {@link JsonObject | JsonObject}. Fails by default if any properties or array elements
52
53
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
53
- * {@link Converters.IJsonConverterContext | context} at runtime.
54
+ * `IJsonConverterContext` at runtime.
54
55
  *
55
56
  * Guaranteed to return a new object.
56
57
  * @public
@@ -86,7 +87,7 @@ exports.jsonObject = new ts_utils_1.Conversion.BaseConverter((from, __self, ctx)
86
87
  * An copying converter which converts a supplied `unknown` value to
87
88
  * a valid {@link JsonArray | JsonArray}. Fails by default if any properties or array elements
88
89
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
89
- * {@link Converters.IJsonConverterContext | context} at runtime.
90
+ * `IJsonConverterContext` at runtime.
90
91
  *
91
92
  * Guaranteed to return a new array.
92
93
  * @public
@@ -124,7 +125,7 @@ exports.jsonArray = new ts_utils_1.Conversion.BaseConverter((from, __self, ctx)
124
125
  * An copying converter which converts a supplied `unknown` value to a
125
126
  * valid {@link JsonValue | JsonValue}. Fails by default if any properties or array elements
126
127
  * are `undefined` - this default behavior can be overridden by supplying an appropriate
127
- * {@link Converters.IJsonConverterContext | context} at runtime.
128
+ * `IJsonConverterContext` at runtime.
128
129
  * @public
129
130
  */
130
131
  exports.jsonValue = new ts_utils_1.Conversion.BaseConverter((from, __self, ctx) => {
@@ -137,28 +138,28 @@ exports.jsonValue = new ts_utils_1.Conversion.BaseConverter((from, __self, ctx)
137
138
  return exports.jsonPrimitive.convert(from, ctx);
138
139
  });
139
140
  /**
140
- * A {@link Converter | Converter} which converts `unknown` to a `string`.
141
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
141
+ * A `StringConverter` which converts `unknown` to a `string`.
142
+ * Accepts `IJsonConverterContext` but ignores it.
142
143
  * @public
143
144
  */
144
145
  exports.string = new ts_utils_1.StringConverter();
145
146
  /**
146
- * A {@link Converter | Converter} which converts `unknown` to a `number`.
147
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
147
+ * A `Converter` which converts `unknown` to a `number`.
148
+ * Accepts `IJsonConverterContext` but ignores it.
148
149
  * Mirrors the behavior of `@fgv/ts-utils`.
149
150
  * @public
150
151
  */
151
152
  exports.number = new ts_utils_1.Conversion.BaseConverter((from) => ts_utils_1.Converters.number.convert(from));
152
153
  /**
153
- * A {@link Converter | Converter} which converts `unknown` to a `boolean`.
154
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
154
+ * A `Converter` which converts `unknown` to a `boolean`.
155
+ * Accepts `IJsonConverterContext` but ignores it.
155
156
  * Mirrors the behavior of `@fgv/ts-utils`.
156
157
  * @public
157
158
  */
158
159
  exports.boolean = new ts_utils_1.Conversion.BaseConverter((from) => ts_utils_1.Converters.boolean.convert(from));
159
160
  /**
160
161
  * Helper to create a converter for a literal value.
161
- * Accepts {@link Converters.IJsonConverterContext | IJsonConverterContext} but ignores it.
162
+ * Accepts `IJsonConverterContext` but ignores it.
162
163
  * Mirrors the behavior of `@fgv/ts-utils`.
163
164
  * @public
164
165
  */
@@ -166,17 +167,17 @@ function literal(value) {
166
167
  return ts_utils_1.Converters.literal(value);
167
168
  }
168
169
  /**
169
- * Helper function to create a {@link Converter | Converter} which converts `unknown` to one of a set of
170
+ * Helper function to create a `Converter` which converts `unknown` to one of a set of
170
171
  * supplied enumerated values. Anything else fails.
171
172
  *
172
173
  * @remarks
173
- * This JSON variant accepts an {@link Converters.IJsonConverterContext | IJsonConverterContext} OR
174
+ * This JSON variant accepts an `IJsonConverterContext` OR
174
175
  * a `ReadonlyArray<T>` as its conversion context. If the context is an array, it is used to override the
175
176
  * allowed values for that conversion; otherwise, the original `values` supplied at creation time are used.
176
177
  *
177
178
  * @param values - Array of allowed values.
178
179
  * @param message - Optional custom failure message.
179
- * @returns A new {@link Converter | Converter} returning `<T>`.
180
+ * @returns A new `Converter` returning `<T>`.
180
181
  * @public
181
182
  */
182
183
  function enumeratedValue(values, message) {
@@ -189,4 +190,26 @@ function enumeratedValue(values, message) {
189
190
  return (0, ts_utils_1.fail)(message !== null && message !== void 0 ? message : `Invalid enumerated value ${JSON.stringify(from)}`);
190
191
  });
191
192
  }
193
+ /**
194
+ * Creates a converter that parses JSON string content and then applies the supplied converter.
195
+ * @param converter - Converter to apply to the parsed JSON
196
+ * @returns Converter that parses JSON then validates
197
+ * @public
198
+ */
199
+ function jsonConverter(converter) {
200
+ return new ts_utils_1.Conversion.BaseConverter((from) => {
201
+ if (typeof from !== 'string') {
202
+ return (0, ts_utils_1.fail)('Input must be a string');
203
+ }
204
+ const parseResult = (0, ts_utils_1.captureResult)(() => JSON.parse(from));
205
+ if (parseResult.isFailure()) {
206
+ return (0, ts_utils_1.fail)(`Failed to parse JSON: ${parseResult.message}`);
207
+ }
208
+ const parsed = parseResult.value;
209
+ if (typeof parsed !== 'object' || parsed === null) {
210
+ return (0, ts_utils_1.fail)('Failed to parse JSON: JSON content must be an object');
211
+ }
212
+ return converter.convert(parsed);
213
+ });
214
+ }
192
215
  //# sourceMappingURL=converters.js.map
@@ -1,20 +1,20 @@
1
1
  import { Result } from '@fgv/ts-utils';
2
- import { FileTreeItem, IFileTreeAccessors, IFileTreeDirectoryItem } from './fileTreeAccessors';
2
+ import { FileTreeItem, IDeleteChildOptions, IFileTreeAccessors, IMutableFileTreeDirectoryItem, IMutableFileTreeFileItem } from './fileTreeAccessors';
3
3
  /**
4
4
  * Class representing a directory in a file tree.
5
5
  * @public
6
6
  */
7
- export declare class DirectoryItem<TCT extends string = string> implements IFileTreeDirectoryItem<TCT> {
7
+ export declare class DirectoryItem<TCT extends string = string> implements IMutableFileTreeDirectoryItem<TCT> {
8
8
  /**
9
- * {@inheritdoc FileTree.IFileTreeDirectoryItem."type"}
9
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem."type"}
10
10
  */
11
11
  readonly type: 'directory';
12
12
  /**
13
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.absolutePath}
13
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.absolutePath}
14
14
  */
15
15
  readonly absolutePath: string;
16
16
  /**
17
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.name}
17
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.name}
18
18
  */
19
19
  get name(): string;
20
20
  /**
@@ -40,8 +40,31 @@ export declare class DirectoryItem<TCT extends string = string> implements IFile
40
40
  */
41
41
  static create<TCT extends string = string>(path: string, hal: IFileTreeAccessors<TCT>): Result<DirectoryItem<TCT>>;
42
42
  /**
43
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.getChildren}
43
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.getChildren}
44
44
  */
45
45
  getChildren(): Result<ReadonlyArray<FileTreeItem<TCT>>>;
46
+ /**
47
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.createChildFile}
48
+ */
49
+ createChildFile(name: string, contents: string): Result<IMutableFileTreeFileItem<TCT>>;
50
+ /**
51
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.createChildDirectory}
52
+ */
53
+ createChildDirectory(name: string): Result<IMutableFileTreeDirectoryItem<TCT>>;
54
+ /**
55
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.deleteChild}
56
+ */
57
+ deleteChild(name: string, options?: IDeleteChildOptions): Result<boolean>;
58
+ /**
59
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.delete}
60
+ */
61
+ delete(): Result<boolean>;
62
+ /**
63
+ * Recursively deletes all children of a directory and then the directory itself.
64
+ * @param dirPath - The absolute path of the directory to delete.
65
+ * @returns `Success` with `true` if the directory was deleted, or `Failure` with an error message.
66
+ * @internal
67
+ */
68
+ private _deleteRecursive;
46
69
  }
47
70
  //# sourceMappingURL=directoryItem.d.ts.map
@@ -23,13 +23,14 @@
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.DirectoryItem = void 0;
25
25
  const ts_utils_1 = require("@fgv/ts-utils");
26
+ const fileTreeAccessors_1 = require("./fileTreeAccessors");
26
27
  /**
27
28
  * Class representing a directory in a file tree.
28
29
  * @public
29
30
  */
30
31
  class DirectoryItem {
31
32
  /**
32
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.name}
33
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.name}
33
34
  */
34
35
  get name() {
35
36
  return this._hal.getBaseName(this.absolutePath);
@@ -43,7 +44,7 @@ class DirectoryItem {
43
44
  */
44
45
  constructor(path, hal) {
45
46
  /**
46
- * {@inheritdoc FileTree.IFileTreeDirectoryItem."type"}
47
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem."type"}
47
48
  */
48
49
  this.type = 'directory';
49
50
  this._hal = hal;
@@ -61,11 +62,105 @@ class DirectoryItem {
61
62
  return (0, ts_utils_1.captureResult)(() => new DirectoryItem(path, hal));
62
63
  }
63
64
  /**
64
- * {@inheritdoc FileTree.IFileTreeDirectoryItem.getChildren}
65
+ * {@inheritDoc FileTree.IFileTreeDirectoryItem.getChildren}
65
66
  */
66
67
  getChildren() {
67
68
  return this._hal.getChildren(this.absolutePath);
68
69
  }
70
+ /**
71
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.createChildFile}
72
+ */
73
+ createChildFile(name, contents) {
74
+ const hal = this._hal;
75
+ if (!(0, fileTreeAccessors_1.isMutableAccessors)(hal)) {
76
+ /* c8 ignore next 2 - defensive: all current accessor implementations support mutation interface */
77
+ return (0, ts_utils_1.fail)(`${this.absolutePath}: mutation not supported`);
78
+ }
79
+ const filePath = hal.joinPaths(this.absolutePath, name);
80
+ return hal.saveFileContents(filePath, contents).onSuccess(() => hal.getItem(filePath).onSuccess((item) => {
81
+ /* c8 ignore next 3 - defensive: verifies accessor returned correct item type after save */
82
+ if (!(0, fileTreeAccessors_1.isMutableFileItem)(item)) {
83
+ return (0, ts_utils_1.fail)(`${filePath}: expected mutable file but got ${item.type}`);
84
+ }
85
+ return (0, ts_utils_1.succeed)(item);
86
+ }));
87
+ }
88
+ /**
89
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.createChildDirectory}
90
+ */
91
+ createChildDirectory(name) {
92
+ const hal = this._hal;
93
+ if (!(0, fileTreeAccessors_1.isMutableAccessors)(hal)) {
94
+ /* c8 ignore next 2 - defensive: all current accessor implementations support mutation interface */
95
+ return (0, ts_utils_1.fail)(`${this.absolutePath}: mutation not supported`);
96
+ }
97
+ const dirPath = hal.joinPaths(this.absolutePath, name);
98
+ return hal.createDirectory(dirPath).onSuccess(() => DirectoryItem.create(dirPath, hal));
99
+ }
100
+ /**
101
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.deleteChild}
102
+ */
103
+ deleteChild(name, options) {
104
+ const hal = this._hal;
105
+ if (!(0, fileTreeAccessors_1.isMutableAccessors)(hal)) {
106
+ /* c8 ignore next 2 - defensive: all current accessor implementations support mutation interface */
107
+ return (0, ts_utils_1.fail)(`${this.absolutePath}: mutation not supported`);
108
+ }
109
+ const childPath = hal.joinPaths(this.absolutePath, name);
110
+ return hal.getItem(childPath).onSuccess((item) => {
111
+ if (item.type === 'file') {
112
+ return hal.deleteFile(childPath);
113
+ }
114
+ // Directory child
115
+ if (options === null || options === void 0 ? void 0 : options.recursive) {
116
+ return this._deleteRecursive(childPath);
117
+ }
118
+ return hal.deleteDirectory(childPath);
119
+ });
120
+ }
121
+ /**
122
+ * {@inheritDoc FileTree.IMutableFileTreeDirectoryItem.delete}
123
+ */
124
+ delete() {
125
+ const hal = this._hal;
126
+ if (!(0, fileTreeAccessors_1.isMutableAccessors)(hal)) {
127
+ /* c8 ignore next 2 - defensive: all current accessor implementations support mutation interface */
128
+ return (0, ts_utils_1.fail)(`${this.absolutePath}: mutation not supported`);
129
+ }
130
+ return hal.deleteDirectory(this.absolutePath);
131
+ }
132
+ /**
133
+ * Recursively deletes all children of a directory and then the directory itself.
134
+ * @param dirPath - The absolute path of the directory to delete.
135
+ * @returns `Success` with `true` if the directory was deleted, or `Failure` with an error message.
136
+ * @internal
137
+ */
138
+ _deleteRecursive(dirPath) {
139
+ const hal = this._hal;
140
+ /* c8 ignore next 3 - defensive: caller already verified mutable */
141
+ if (!(0, fileTreeAccessors_1.isMutableAccessors)(hal)) {
142
+ return (0, ts_utils_1.fail)(`${dirPath}: mutation not supported`);
143
+ }
144
+ return hal.getChildren(dirPath).onSuccess((children) => {
145
+ for (const child of children) {
146
+ if (child.type === 'file') {
147
+ const result = hal.deleteFile(child.absolutePath);
148
+ /* c8 ignore next 3 - defensive: error propagation during recursive delete */
149
+ if (result.isFailure()) {
150
+ return result;
151
+ }
152
+ }
153
+ else {
154
+ const result = this._deleteRecursive(child.absolutePath);
155
+ /* c8 ignore next 3 - defensive: error propagation during recursive delete */
156
+ if (result.isFailure()) {
157
+ return result;
158
+ }
159
+ }
160
+ }
161
+ return hal.deleteDirectory(dirPath);
162
+ });
163
+ }
69
164
  }
70
165
  exports.DirectoryItem = DirectoryItem;
71
166
  //# sourceMappingURL=directoryItem.js.map
@@ -1,34 +1,34 @@
1
- import { Result } from '@fgv/ts-utils';
1
+ import { DetailedResult, Result } from '@fgv/ts-utils';
2
2
  import { Converter, Validator } from '@fgv/ts-utils';
3
3
  import { JsonValue } from '../json';
4
- import { IFileTreeAccessors, IFileTreeFileItem } from './fileTreeAccessors';
4
+ import { IFileTreeAccessors, IMutableFileTreeFileItem, SaveDetail } from './fileTreeAccessors';
5
5
  /**
6
6
  * Class representing a file in a file tree.
7
7
  * @public
8
8
  */
9
- export declare class FileItem<TCT extends string = string> implements IFileTreeFileItem<TCT> {
9
+ export declare class FileItem<TCT extends string = string> implements IMutableFileTreeFileItem<TCT> {
10
10
  /**
11
- * {@inheritdoc FileTree.IFileTreeFileItem."type"}
11
+ * {@inheritDoc FileTree.IFileTreeFileItem."type"}
12
12
  */
13
13
  readonly type: 'file';
14
14
  /**
15
- * {@inheritdoc FileTree.IFileTreeFileItem.absolutePath}
15
+ * {@inheritDoc FileTree.IFileTreeFileItem.absolutePath}
16
16
  */
17
17
  readonly absolutePath: string;
18
18
  /**
19
- * {@inheritdoc FileTree.IFileTreeFileItem.name}
19
+ * {@inheritDoc FileTree.IFileTreeFileItem.name}
20
20
  */
21
21
  get name(): string;
22
22
  /**
23
- * {@inheritdoc FileTree.IFileTreeFileItem.baseName}
23
+ * {@inheritDoc FileTree.IFileTreeFileItem.baseName}
24
24
  */
25
25
  get baseName(): string;
26
26
  /**
27
- * {@inheritdoc FileTree.IFileTreeFileItem.extension}
27
+ * {@inheritDoc FileTree.IFileTreeFileItem.extension}
28
28
  */
29
29
  get extension(): string;
30
30
  /**
31
- * {@inheritdoc FileTree.IFileTreeFileItem.contentType}
31
+ * {@inheritDoc FileTree.IFileTreeFileItem.contentType}
32
32
  */
33
33
  get contentType(): TCT | undefined;
34
34
  /**
@@ -58,15 +58,19 @@ export declare class FileItem<TCT extends string = string> implements IFileTreeF
58
58
  */
59
59
  static create<TCT extends string = string>(path: string, hal: IFileTreeAccessors<TCT>): Result<FileItem<TCT>>;
60
60
  /**
61
- * {@inheritdoc FileTree.IFileTreeFileItem.(getContents:1)}
61
+ * {@inheritDoc FileTree.IFileTreeFileItem.getIsMutable}
62
+ */
63
+ getIsMutable(): DetailedResult<boolean, SaveDetail>;
64
+ /**
65
+ * {@inheritDoc FileTree.IFileTreeFileItem.getContents}
62
66
  */
63
67
  getContents(): Result<JsonValue>;
64
68
  /**
65
- * {@inheritdoc FileTree.IFileTreeFileItem.(getContents:2)}
69
+ * {@inheritDoc FileTree.IFileTreeFileItem.getContents}
66
70
  */
67
71
  getContents<T>(converter: Validator<T> | Converter<T>): Result<T>;
68
72
  /**
69
- * {@inheritdoc FileTree.IFileTreeFileItem.getRawContents}
73
+ * {@inheritDoc FileTree.IFileTreeFileItem.getRawContents}
70
74
  */
71
75
  getRawContents(): Result<string>;
72
76
  /**
@@ -74,15 +78,28 @@ export declare class FileItem<TCT extends string = string> implements IFileTreeF
74
78
  * @param contentType - The content type of the file.
75
79
  */
76
80
  setContentType(contentType: TCT | undefined): void;
81
+ /**
82
+ * {@inheritDoc FileTree.IFileTreeFileItem.setContents}
83
+ */
84
+ setContents(json: JsonValue): Result<JsonValue>;
85
+ /**
86
+ * {@inheritDoc FileTree.IFileTreeFileItem.setRawContents}
87
+ */
88
+ setRawContents(contents: string): Result<string>;
89
+ /**
90
+ * {@inheritDoc FileTree.IFileTreeFileItem.delete}
91
+ */
92
+ delete(): Result<boolean>;
77
93
  /**
78
94
  * Default function to infer the content type of a file.
79
95
  * @param filePath - The path of the file.
96
+ * @param provided - Optional supplied content type.
80
97
  * @returns `Success` with the content type of the file if successful, or
81
98
  * `Failure` with an error message otherwise.
82
99
  * @remarks This default implementation always returns `Success` with `undefined`.
83
100
  * @public
84
101
  */
85
- static defaultInferContentType<TCT extends string = string>(__filePath: string, __provided?: string): Result<TCT | undefined>;
102
+ static defaultInferContentType<TCT extends string = string>(filePath: string, provided?: string): Result<TCT | undefined>;
86
103
  /**
87
104
  * Default function to accept the content type of a file.
88
105
  * @param filePath - The path of the file.
@@ -92,6 +109,6 @@ export declare class FileItem<TCT extends string = string> implements IFileTreeF
92
109
  * @remarks This default implementation always returns `Success` with `undefined`.
93
110
  * @public
94
111
  */
95
- static defaultAcceptContentType<TCT extends string = string>(__filePath: string, provided?: TCT): Result<TCT | undefined>;
112
+ static defaultAcceptContentType<TCT extends string = string>(filePath: string, provided?: TCT): Result<TCT | undefined>;
96
113
  }
97
114
  //# sourceMappingURL=fileItem.d.ts.map
@@ -23,31 +23,32 @@
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.FileItem = void 0;
25
25
  const ts_utils_1 = require("@fgv/ts-utils");
26
+ const fileTreeAccessors_1 = require("./fileTreeAccessors");
26
27
  /**
27
28
  * Class representing a file in a file tree.
28
29
  * @public
29
30
  */
30
31
  class FileItem {
31
32
  /**
32
- * {@inheritdoc FileTree.IFileTreeFileItem.name}
33
+ * {@inheritDoc FileTree.IFileTreeFileItem.name}
33
34
  */
34
35
  get name() {
35
36
  return this._hal.getBaseName(this.absolutePath);
36
37
  }
37
38
  /**
38
- * {@inheritdoc FileTree.IFileTreeFileItem.baseName}
39
+ * {@inheritDoc FileTree.IFileTreeFileItem.baseName}
39
40
  */
40
41
  get baseName() {
41
42
  return this._hal.getBaseName(this.absolutePath, this.extension);
42
43
  }
43
44
  /**
44
- * {@inheritdoc FileTree.IFileTreeFileItem.extension}
45
+ * {@inheritDoc FileTree.IFileTreeFileItem.extension}
45
46
  */
46
47
  get extension() {
47
48
  return this._hal.getExtension(this.absolutePath);
48
49
  }
49
50
  /**
50
- * {@inheritdoc FileTree.IFileTreeFileItem.contentType}
51
+ * {@inheritDoc FileTree.IFileTreeFileItem.contentType}
51
52
  */
52
53
  get contentType() {
53
54
  return this._contentType;
@@ -61,7 +62,7 @@ class FileItem {
61
62
  */
62
63
  constructor(path, hal) {
63
64
  /**
64
- * {@inheritdoc FileTree.IFileTreeFileItem."type"}
65
+ * {@inheritDoc FileTree.IFileTreeFileItem."type"}
65
66
  */
66
67
  this.type = 'file';
67
68
  this._hal = hal;
@@ -78,6 +79,16 @@ class FileItem {
78
79
  static create(path, hal) {
79
80
  return (0, ts_utils_1.captureResult)(() => new FileItem(path, hal));
80
81
  }
82
+ /**
83
+ * {@inheritDoc FileTree.IFileTreeFileItem.getIsMutable}
84
+ */
85
+ getIsMutable() {
86
+ if ((0, fileTreeAccessors_1.isMutableAccessors)(this._hal)) {
87
+ return this._hal.fileIsMutable(this.absolutePath);
88
+ }
89
+ /* c8 ignore next 2 - defensive: all current accessor implementations support mutation interface */
90
+ return (0, ts_utils_1.failWithDetail)(`${this.absolutePath}: mutation not supported`, 'not-supported');
91
+ }
81
92
  getContents(converter) {
82
93
  return this._hal
83
94
  .getFileContents(this.absolutePath)
@@ -90,7 +101,7 @@ class FileItem {
90
101
  });
91
102
  }
92
103
  /**
93
- * {@inheritdoc FileTree.IFileTreeFileItem.getRawContents}
104
+ * {@inheritDoc FileTree.IFileTreeFileItem.getRawContents}
94
105
  */
95
106
  getRawContents() {
96
107
  return this._hal.getFileContents(this.absolutePath);
@@ -102,15 +113,42 @@ class FileItem {
102
113
  setContentType(contentType) {
103
114
  this._contentType = contentType;
104
115
  }
116
+ /**
117
+ * {@inheritDoc FileTree.IFileTreeFileItem.setContents}
118
+ */
119
+ setContents(json) {
120
+ return (0, ts_utils_1.captureResult)(() => JSON.stringify(json, null, 2)).onSuccess((contents) => this.setRawContents(contents).onSuccess(() => ts_utils_1.Success.with(json)));
121
+ }
122
+ /**
123
+ * {@inheritDoc FileTree.IFileTreeFileItem.setRawContents}
124
+ */
125
+ setRawContents(contents) {
126
+ if ((0, fileTreeAccessors_1.isMutableAccessors)(this._hal)) {
127
+ return this._hal.saveFileContents(this.absolutePath, contents);
128
+ }
129
+ /* c8 ignore next 2 - defensive: all current accessor implementations support mutation interface */
130
+ return (0, ts_utils_1.fail)(`${this.absolutePath}: mutation not supported`);
131
+ }
132
+ /**
133
+ * {@inheritDoc FileTree.IFileTreeFileItem.delete}
134
+ */
135
+ delete() {
136
+ if (!(0, fileTreeAccessors_1.isMutableAccessors)(this._hal)) {
137
+ /* c8 ignore next 2 - defensive: all current accessor implementations support mutation interface */
138
+ return (0, ts_utils_1.fail)(`${this.absolutePath}: mutation not supported`);
139
+ }
140
+ return this._hal.deleteFile(this.absolutePath);
141
+ }
105
142
  /**
106
143
  * Default function to infer the content type of a file.
107
144
  * @param filePath - The path of the file.
145
+ * @param provided - Optional supplied content type.
108
146
  * @returns `Success` with the content type of the file if successful, or
109
147
  * `Failure` with an error message otherwise.
110
148
  * @remarks This default implementation always returns `Success` with `undefined`.
111
149
  * @public
112
150
  */
113
- static defaultInferContentType(__filePath, __provided) {
151
+ static defaultInferContentType(filePath, provided) {
114
152
  return (0, ts_utils_1.succeed)(undefined);
115
153
  }
116
154
  /**
@@ -122,7 +160,7 @@ class FileItem {
122
160
  * @remarks This default implementation always returns `Success` with `undefined`.
123
161
  * @public
124
162
  */
125
- static defaultAcceptContentType(__filePath, provided) {
163
+ static defaultAcceptContentType(filePath, provided) {
126
164
  return (0, ts_utils_1.succeed)(provided);
127
165
  }
128
166
  }