@weborigami/async-tree 0.5.3 → 0.5.5

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 (159) hide show
  1. package/index.ts +16 -6
  2. package/package.json +2 -2
  3. package/shared.js +20 -30
  4. package/src/Tree.js +62 -502
  5. package/src/constants.js +2 -0
  6. package/src/drivers/BrowserFileTree.js +9 -10
  7. package/src/drivers/DeepMapTree.js +3 -3
  8. package/src/drivers/DeepObjectTree.js +4 -5
  9. package/src/drivers/DeferredTree.js +2 -2
  10. package/src/drivers/FileTree.js +11 -33
  11. package/src/drivers/FunctionTree.js +1 -1
  12. package/src/drivers/MapTree.js +6 -6
  13. package/src/drivers/ObjectTree.js +4 -3
  14. package/src/drivers/SetTree.js +1 -1
  15. package/src/drivers/SiteTree.js +1 -1
  16. package/src/drivers/constantTree.js +1 -1
  17. package/src/extension.js +5 -3
  18. package/src/jsonKeys.js +5 -7
  19. package/src/operations/addNextPrevious.js +10 -9
  20. package/src/operations/assign.js +40 -0
  21. package/src/operations/cache.js +18 -12
  22. package/src/operations/cachedKeyFunctions.js +15 -4
  23. package/src/operations/clear.js +20 -0
  24. package/src/operations/concat.js +17 -0
  25. package/src/operations/deepMap.js +25 -0
  26. package/src/operations/deepMerge.js +11 -25
  27. package/src/operations/deepReverse.js +6 -7
  28. package/src/operations/deepTake.js +6 -7
  29. package/src/operations/deepText.js +4 -4
  30. package/src/operations/deepValuesIterator.js +8 -6
  31. package/src/operations/defineds.js +32 -0
  32. package/src/operations/delete.js +20 -0
  33. package/src/operations/entries.js +16 -0
  34. package/src/operations/extensionKeyFunctions.js +1 -1
  35. package/src/operations/filter.js +7 -8
  36. package/src/operations/first.js +18 -0
  37. package/src/operations/forEach.js +20 -0
  38. package/src/operations/from.js +77 -0
  39. package/src/operations/fromFn.js +26 -0
  40. package/src/operations/globKeys.js +8 -8
  41. package/src/operations/group.js +9 -7
  42. package/src/operations/has.js +16 -0
  43. package/src/operations/indent.js +4 -2
  44. package/src/operations/inners.js +29 -0
  45. package/src/operations/invokeFunctions.js +5 -4
  46. package/src/operations/isAsyncMutableTree.js +15 -0
  47. package/src/operations/isAsyncTree.js +21 -0
  48. package/src/operations/isTraversable.js +15 -0
  49. package/src/operations/isTreelike.js +33 -0
  50. package/src/operations/json.js +4 -3
  51. package/src/operations/keys.js +14 -0
  52. package/src/operations/length.js +15 -0
  53. package/src/operations/map.js +157 -95
  54. package/src/operations/mapExtension.js +27 -0
  55. package/src/operations/mapReduce.js +44 -0
  56. package/src/operations/mask.js +18 -16
  57. package/src/operations/match.js +74 -0
  58. package/src/operations/merge.js +22 -20
  59. package/src/operations/paginate.js +3 -5
  60. package/src/operations/parent.js +13 -0
  61. package/src/operations/paths.js +51 -0
  62. package/src/operations/plain.js +34 -0
  63. package/src/operations/regExpKeys.js +4 -5
  64. package/src/operations/remove.js +14 -0
  65. package/src/operations/reverse.js +4 -6
  66. package/src/operations/root.js +17 -0
  67. package/src/operations/scope.js +4 -6
  68. package/src/operations/setDeep.js +50 -0
  69. package/src/operations/shuffle.js +46 -0
  70. package/src/operations/sort.js +19 -12
  71. package/src/operations/take.js +3 -5
  72. package/src/operations/text.js +3 -3
  73. package/src/operations/toFunction.js +14 -0
  74. package/src/operations/traverse.js +25 -0
  75. package/src/operations/traverseOrThrow.js +64 -0
  76. package/src/operations/traversePath.js +16 -0
  77. package/src/operations/values.js +15 -0
  78. package/src/operations/withKeys.js +33 -0
  79. package/src/utilities/TypedArray.js +2 -0
  80. package/src/utilities/box.js +20 -0
  81. package/src/utilities/castArraylike.js +38 -0
  82. package/src/utilities/getParent.js +33 -0
  83. package/src/utilities/getRealmObjectPrototype.js +19 -0
  84. package/src/utilities/getTreeArgument.js +43 -0
  85. package/src/utilities/isPacked.js +20 -0
  86. package/src/utilities/isPlainObject.js +29 -0
  87. package/src/utilities/isPrimitive.js +13 -0
  88. package/src/utilities/isStringlike.js +25 -0
  89. package/src/utilities/isUnpackable.js +13 -0
  90. package/src/utilities/keysFromPath.js +34 -0
  91. package/src/utilities/naturalOrder.js +9 -0
  92. package/src/utilities/pathFromKeys.js +18 -0
  93. package/src/utilities/setParent.js +38 -0
  94. package/src/utilities/toFunction.js +41 -0
  95. package/src/utilities/toPlainValue.js +95 -0
  96. package/src/utilities/toString.js +37 -0
  97. package/test/drivers/ExplorableSiteTree.test.js +1 -1
  98. package/test/drivers/FileTree.test.js +1 -1
  99. package/test/drivers/calendarTree.test.js +1 -1
  100. package/test/jsonKeys.test.js +1 -1
  101. package/test/operations/assign.test.js +54 -0
  102. package/test/operations/cache.test.js +1 -1
  103. package/test/operations/cachedKeyFunctions.test.js +16 -16
  104. package/test/operations/clear.test.js +34 -0
  105. package/test/operations/deepMap.test.js +29 -0
  106. package/test/operations/deepMerge.test.js +2 -6
  107. package/test/operations/deepReverse.test.js +1 -1
  108. package/test/operations/defineds.test.js +25 -0
  109. package/test/operations/delete.test.js +20 -0
  110. package/test/operations/entries.test.js +18 -0
  111. package/test/operations/extensionKeyFunctions.test.js +10 -10
  112. package/test/operations/first.test.js +15 -0
  113. package/test/operations/fixtures/README.md +1 -0
  114. package/test/operations/forEach.test.js +22 -0
  115. package/test/operations/from.test.js +67 -0
  116. package/test/operations/globKeys.test.js +3 -3
  117. package/test/operations/has.test.js +15 -0
  118. package/test/operations/inners.test.js +30 -0
  119. package/test/operations/invokeFunctions.test.js +1 -1
  120. package/test/operations/isAsyncMutableTree.test.js +17 -0
  121. package/test/operations/isAsyncTree.test.js +26 -0
  122. package/test/operations/isTreelike.test.js +13 -0
  123. package/test/operations/keys.test.js +15 -0
  124. package/test/operations/length.test.js +15 -0
  125. package/test/operations/map.test.js +62 -45
  126. package/test/operations/mapExtension.test.js +0 -0
  127. package/test/operations/mapReduce.test.js +23 -0
  128. package/test/operations/mask.test.js +1 -1
  129. package/test/operations/match.test.js +33 -0
  130. package/test/operations/merge.test.js +23 -9
  131. package/test/operations/paginate.test.js +2 -1
  132. package/test/operations/parent.test.js +15 -0
  133. package/test/operations/paths.test.js +40 -0
  134. package/test/operations/plain.test.js +69 -0
  135. package/test/operations/reverse.test.js +1 -1
  136. package/test/operations/scope.test.js +1 -1
  137. package/test/operations/setDeep.test.js +53 -0
  138. package/test/operations/shuffle.test.js +18 -0
  139. package/test/operations/sort.test.js +3 -3
  140. package/test/operations/toFunction.test.js +16 -0
  141. package/test/operations/traverse.test.js +43 -0
  142. package/test/operations/traversePath.test.js +16 -0
  143. package/test/operations/values.test.js +18 -0
  144. package/test/operations/withKeys.test.js +21 -0
  145. package/test/utilities/box.test.js +26 -0
  146. package/test/utilities/getRealmObjectPrototype.test.js +11 -0
  147. package/test/utilities/isPlainObject.test.js +13 -0
  148. package/test/utilities/keysFromPath.test.js +14 -0
  149. package/test/utilities/naturalOrder.test.js +11 -0
  150. package/test/utilities/pathFromKeys.test.js +12 -0
  151. package/test/utilities/setParent.test.js +34 -0
  152. package/test/utilities/toFunction.test.js +34 -0
  153. package/test/utilities/toPlainValue.test.js +27 -0
  154. package/test/utilities/toString.test.js +22 -0
  155. package/src/Tree.d.ts +0 -24
  156. package/src/utilities.d.ts +0 -21
  157. package/src/utilities.js +0 -439
  158. package/test/Tree.test.js +0 -407
  159. package/test/utilities.test.js +0 -141
package/src/Tree.js CHANGED
@@ -1,504 +1,64 @@
1
- import DeferredTree from "./drivers/DeferredTree.js";
2
- import FunctionTree from "./drivers/FunctionTree.js";
3
- import MapTree from "./drivers/MapTree.js";
4
- import SetTree from "./drivers/SetTree.js";
5
- import { DeepObjectTree, ObjectTree } from "./internal.js";
6
- import * as symbols from "./symbols.js";
7
- import * as trailingSlash from "./trailingSlash.js";
8
- import TraverseError from "./TraverseError.js";
9
- import * as utilities from "./utilities.js";
10
- import {
11
- castArrayLike,
12
- isPacked,
13
- isPlainObject,
14
- isUnpackable,
15
- toPlainValue,
16
- } from "./utilities.js";
17
-
18
- /**
19
- * Helper functions for working with async trees
20
- *
21
- * @typedef {import("../index.ts").PlainObject} PlainObject
22
- * @typedef {import("../index.ts").ReduceFn} ReduceFn
23
- * @typedef {import("../index.ts").Treelike} Treelike
24
- * @typedef {import("../index.ts").ValueKeyFn} ValueKeyFn
25
- * @typedef {import("@weborigami/types").AsyncMutableTree} AsyncMutableTree
26
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
27
- */
28
-
29
- /**
30
- * Apply the key/values pairs from the source tree to the target tree.
31
- *
32
- * If a key exists in both trees, and the values in both trees are
33
- * subtrees, then the subtrees will be merged recursively. Otherwise, the
34
- * value from the source tree will overwrite the value in the target tree.
35
- *
36
- * @param {AsyncMutableTree} target
37
- * @param {AsyncTree} source
38
- */
39
- export async function assign(target, source) {
40
- const targetTree = from(target);
41
- const sourceTree = from(source);
42
- if (!isAsyncMutableTree(targetTree)) {
43
- throw new TypeError("Target must be a mutable asynchronous tree");
44
- }
45
- // Fire off requests to update all keys, then wait for all of them to finish.
46
- const keys = Array.from(await sourceTree.keys());
47
- const promises = keys.map(async (key) => {
48
- const sourceValue = await sourceTree.get(key);
49
- if (isAsyncTree(sourceValue)) {
50
- const targetValue = await targetTree.get(key);
51
- if (isAsyncMutableTree(targetValue)) {
52
- // Both source and target are trees; recurse.
53
- await assign(targetValue, sourceValue);
54
- return;
55
- }
56
- }
57
- // Copy the value from the source to the target.
58
- await /** @type {any} */ (targetTree).set(key, sourceValue);
59
- });
60
- await Promise.all(promises);
61
- return targetTree;
62
- }
63
-
64
- /**
65
- * Removes all entries from the tree.
66
- *
67
- * @param {AsyncMutableTree} tree
68
- */
69
- export async function clear(tree) {
70
- const keys = Array.from(await tree.keys());
71
- const promises = keys.map((key) => tree.set(key, undefined));
72
- await Promise.all(promises);
73
- }
74
-
75
- /**
76
- * Returns a new `Iterator` object that contains a two-member array of `[key,
77
- * value]` for each element in the specific node of the tree.
78
- *
79
- * @param {AsyncTree} tree
80
- */
81
- export async function entries(tree) {
82
- const keys = Array.from(await tree.keys());
83
- const promises = keys.map(async (key) => [key, await tree.get(key)]);
84
- return Promise.all(promises);
85
- }
86
-
87
- /**
88
- * Calls callbackFn once for each key-value pair present in the specific node of
89
- * the tree.
90
- *
91
- * @param {AsyncTree} tree
92
- * @param {Function} callbackFn
93
- */
94
- export async function forEach(tree, callbackFn) {
95
- const keys = Array.from(await tree.keys());
96
- const promises = keys.map(async (key) => {
97
- const value = await tree.get(key);
98
- return callbackFn(value, key);
99
- });
100
- await Promise.all(promises);
101
- }
102
-
103
- /**
104
- * Attempts to cast the indicated object to an async tree.
105
- *
106
- * If the object is a plain object, it will be converted to an ObjectTree. The
107
- * optional `deep` option can be set to `true` to convert a plain object to a
108
- * DeepObjectTree. The optional `parent` parameter will be used as the default
109
- * parent of the new tree.
110
- *
111
- * @param {Treelike | Object} object
112
- * @param {{ deep?: boolean, parent?: AsyncTree|null }} [options]
113
- * @returns {AsyncTree}
114
- */
115
- export function from(object, options = {}) {
116
- const deep = options.deep ?? object[symbols.deep];
117
- let tree;
118
- if (isAsyncTree(object)) {
119
- // Argument already supports the tree interface.
120
- // @ts-ignore
121
- return object;
122
- } else if (typeof object === "function") {
123
- tree = new FunctionTree(object);
124
- } else if (object instanceof Map) {
125
- tree = new MapTree(object);
126
- } else if (object instanceof Set) {
127
- tree = new SetTree(object);
128
- } else if (isPlainObject(object) || object instanceof Array) {
129
- tree = deep ? new DeepObjectTree(object) : new ObjectTree(object);
130
- } else if (isUnpackable(object)) {
131
- async function AsyncFunction() {} // Sample async function
132
- tree =
133
- object.unpack instanceof AsyncFunction.constructor
134
- ? // Async unpack: return a deferred tree.
135
- new DeferredTree(object.unpack, { deep })
136
- : // Synchronous unpack: cast the result of unpack() to a tree.
137
- from(object.unpack());
138
- } else if (object && typeof object === "object") {
139
- // An instance of some class.
140
- tree = new ObjectTree(object);
141
- } else if (
142
- typeof object === "string" ||
143
- typeof object === "number" ||
144
- typeof object === "boolean"
145
- ) {
146
- // A primitive value; box it into an object and construct a tree.
147
- const boxed = utilities.box(object);
148
- tree = new ObjectTree(boxed);
149
- } else {
150
- throw new TypeError("Couldn't convert argument to an async tree");
151
- }
152
-
153
- if (!tree.parent && options.parent) {
154
- tree.parent = options.parent;
155
- }
156
- return tree;
157
- }
158
-
159
- /**
160
- * Returns a boolean indicating whether the specific node of the tree has a
161
- * value for the given `key`.
162
- *
163
- * @param {AsyncTree} tree
164
- * @param {any} key
165
- */
166
- export async function has(tree, key) {
167
- const value = await tree.get(key);
168
- return value !== undefined;
169
- }
170
-
171
- /**
172
- * Return true if the indicated object is an async tree.
173
- *
174
- * @param {any} obj
175
- * @returns {obj is AsyncTree}
176
- */
177
- export function isAsyncTree(obj) {
178
- return (
179
- obj !== null &&
180
- typeof obj === "object" &&
181
- typeof obj.get === "function" &&
182
- typeof obj.keys === "function" &&
183
- // JavaScript Map look like trees but can't be extended the same way, so we
184
- // report false.
185
- !(obj instanceof Map)
186
- );
187
- }
188
-
189
- /**
190
- * Return true if the indicated object is an async mutable tree.
191
- *
192
- * @param {any} obj
193
- * @returns {obj is AsyncMutableTree}
194
- */
195
- export function isAsyncMutableTree(obj) {
196
- return (
197
- isAsyncTree(obj) && typeof (/** @type {any} */ (obj).set) === "function"
198
- );
199
- }
200
-
201
1
  /**
202
- * Return true if the object can be traversed via the `traverse()` method. The
203
- * object must be either treelike or a packed object with an `unpack()` method.
204
- *
205
- * @param {any} object
206
- */
207
- export function isTraversable(object) {
208
- return (
209
- isTreelike(object) ||
210
- (isPacked(object) && /** @type {any} */ (object).unpack instanceof Function)
211
- );
212
- }
213
-
214
- /**
215
- * Returns true if the indicated object can be directly treated as an
216
- * asynchronous tree. This includes:
217
- *
218
- * - An object that implements the AsyncTree interface (including
219
- * AsyncTree instances)
220
- * - A function
221
- * - An `Array` instance
222
- * - A `Map` instance
223
- * - A `Set` instance
224
- * - A plain object
225
- *
226
- * Note: the `from()` method accepts any JavaScript object, but `isTreelike`
227
- * returns `false` for an object that isn't one of the above types.
228
- *
229
- * @param {any} obj
230
- * @returns {obj is Treelike}
231
- */
232
- export function isTreelike(obj) {
233
- return (
234
- isAsyncTree(obj) ||
235
- obj instanceof Array ||
236
- obj instanceof Function ||
237
- obj instanceof Map ||
238
- obj instanceof Set ||
239
- isPlainObject(obj)
240
- );
241
- }
242
-
243
- /**
244
- * Return a new tree with deeply-mapped values of the original tree.
245
- *
246
- * @param {Treelike} treelike
247
- * @param {ValueKeyFn} valueFn
248
- */
2
+ * Collection of functions for working with async trees
3
+ */
4
+
5
+ export { default as calendar } from "./drivers/calendarTree.js";
6
+ export { default as constant } from "./drivers/constantTree.js";
7
+ export { default as addNextPrevious } from "./operations/addNextPrevious.js";
8
+ export { default as assign } from "./operations/assign.js";
9
+ export { default as cache } from "./operations/cache.js";
10
+ export { default as clear } from "./operations/clear.js";
11
+ export { default as concat } from "./operations/concat.js";
12
+ export { default as deepMap } from "./operations/deepMap.js";
13
+ export { default as deepMerge } from "./operations/deepMerge.js";
14
+ export { default as deepReverse } from "./operations/deepReverse.js";
15
+ export { default as deepTake } from "./operations/deepTake.js";
16
+ export { default as deepText } from "./operations/deepText.js";
17
+ export { default as deepValues } from "./operations/deepValues.js";
18
+ export { default as deepValuesIterator } from "./operations/deepValuesIterator.js";
19
+ export { default as defineds } from "./operations/defineds.js";
20
+ export { default as delete } from "./operations/delete.js";
21
+ export { default as entries } from "./operations/entries.js";
22
+ export { default as filter } from "./operations/filter.js";
23
+ export { default as first } from "./operations/first.js";
24
+ export { default as forEach } from "./operations/forEach.js";
25
+ export { default as from } from "./operations/from.js";
26
+ export { default as fromFn } from "./operations/fromFn.js";
27
+ export { default as globKeys } from "./operations/globKeys.js";
28
+ export { default as group } from "./operations/group.js";
29
+ export { default as has } from "./operations/has.js";
30
+ export { default as indent } from "./operations/indent.js";
31
+ export { default as inners } from "./operations/inners.js";
32
+ export { default as invokeFunctions } from "./operations/invokeFunctions.js";
33
+ export { default as isAsyncMutableTree } from "./operations/isAsyncMutableTree.js";
34
+ export { default as isAsyncTree } from "./operations/isAsyncTree.js";
35
+ export { default as isTraversable } from "./operations/isTraversable.js";
36
+ export { default as isTreelike } from "./operations/isTreelike.js";
37
+ export { default as json } from "./operations/json.js";
38
+ export { default as keys } from "./operations/keys.js";
39
+ export { default as length } from "./operations/length.js";
249
40
  export { default as map } from "./operations/map.js";
250
-
251
- /**
252
- * Map and reduce a tree.
253
- *
254
- * This is done in as parallel fashion as possible. Each of the tree's values
255
- * will be requested in an async call, then those results will be awaited
256
- * collectively. If a mapFn is provided, it will be invoked to convert each
257
- * value to a mapped value; otherwise, values will be used as is. When the
258
- * values have been obtained, all the values and keys will be passed to the
259
- * reduceFn, which should consolidate those into a single result.
260
- *
261
- * @param {Treelike} treelike
262
- * @param {ValueKeyFn|null} valueFn
263
- * @param {ReduceFn} reduceFn
264
- */
265
- export async function mapReduce(treelike, valueFn, reduceFn) {
266
- const tree = from(treelike);
267
-
268
- // We're going to fire off all the get requests in parallel, as quickly as
269
- // the keys come in. We call the tree's `get` method for each key, but
270
- // *don't* wait for it yet.
271
- const keys = Array.from(await tree.keys());
272
- const promises = keys.map((key) =>
273
- tree.get(key).then((value) =>
274
- // If the value is a subtree, recurse.
275
- isAsyncTree(value)
276
- ? mapReduce(value, valueFn, reduceFn)
277
- : valueFn
278
- ? valueFn(value, key, tree)
279
- : value
280
- )
281
- );
282
-
283
- // Wait for all the promises to resolve. Because the promises were captured
284
- // in the same order as the keys, the values will also be in the same order.
285
- const values = await Promise.all(promises);
286
-
287
- // Reduce the values to a single result.
288
- return reduceFn(values, keys, tree);
289
- }
290
-
291
- /**
292
- * Returns slash-separated paths for all values in the tree.
293
- *
294
- * The `base` argument is prepended to all paths.
295
- *
296
- * If `assumeSlashes` is true, then keys are assumed to have trailing slashes to
297
- * indicate subtrees. The default value of this option is false.
298
- *
299
- * @param {Treelike} treelike
300
- * @param {{ assumeSlashes?: boolean, base?: string }} options
301
- */
302
- export async function paths(treelike, options = {}) {
303
- const tree = from(treelike);
304
- const base = options.base ?? "";
305
- const assumeSlashes = options.assumeSlashes ?? false;
306
- const result = [];
307
- for (const key of await tree.keys()) {
308
- const separator = trailingSlash.has(base) ? "" : "/";
309
- const valuePath = base ? `${base}${separator}${key}` : key;
310
- let isSubtree;
311
- let value;
312
- if (assumeSlashes) {
313
- // Subtree needs to have a trailing slash
314
- isSubtree = trailingSlash.has(key);
315
- if (isSubtree) {
316
- // We'll need the value to recurse
317
- value = await tree.get(key);
318
- }
319
- } else {
320
- // Get value and check
321
- value = await tree.get(key);
322
- }
323
- if (value) {
324
- // If we got the value we can check if it's a subtree
325
- isSubtree = isAsyncTree(value);
326
- }
327
- if (isSubtree) {
328
- const subPaths = await paths(value, { assumeSlashes, base: valuePath });
329
- result.push(...subPaths);
330
- } else {
331
- result.push(valuePath);
332
- }
333
- }
334
- return result;
335
- }
336
-
337
- /**
338
- * Converts an asynchronous tree into a synchronous plain JavaScript object.
339
- *
340
- * The result's keys will be the tree's keys cast to strings. Any tree value
341
- * that is itself a tree will be similarly converted to a plain object.
342
- *
343
- * Any trailing slashes in keys will be removed.
344
- *
345
- * @param {Treelike} treelike
346
- * @returns {Promise<PlainObject|Array>}
347
- */
348
- export async function plain(treelike) {
349
- return mapReduce(treelike, toPlainValue, (values, keys, tree) => {
350
- // Special case for an empty tree: if based on array, return array.
351
- if (tree instanceof ObjectTree && keys.length === 0) {
352
- return /** @type {any} */ (tree).object instanceof Array ? [] : {};
353
- }
354
- // Normalize slashes in keys.
355
- keys = keys.map(trailingSlash.remove);
356
- return castArrayLike(keys, values);
357
- });
358
- }
359
-
360
- /**
361
- * Removes the value for the given key from the specific node of the tree.
362
- *
363
- * Note: The corresponding `Map` method is `delete`, not `remove`. However,
364
- * `delete` is a reserved word in JavaScript, so this uses `remove` instead.
365
- *
366
- * @param {AsyncMutableTree} tree
367
- * @param {any} key
368
- */
369
- export async function remove(tree, key) {
370
- const exists = await has(tree, key);
371
- if (exists) {
372
- await tree.set(key, undefined);
373
- return true;
374
- } else {
375
- return false;
376
- }
377
- }
378
-
379
- /**
380
- * Walk up the `parent` chain to find the root of the tree.
381
- *
382
- * @param {AsyncTree} tree
383
- */
384
- export function root(tree) {
385
- let current = from(tree);
386
- while (current.parent || current[symbols.parent]) {
387
- current = current.parent || current[symbols.parent];
388
- }
389
- return current;
390
- }
391
-
392
- /**
393
- * Returns a function that invokes the tree's `get` method.
394
- *
395
- * @param {Treelike} treelike
396
- * @returns {Function}
397
- */
398
- export function toFunction(treelike) {
399
- const tree = from(treelike);
400
- return tree.get.bind(tree);
401
- }
402
-
403
- /**
404
- * Return the value at the corresponding path of keys.
405
- *
406
- * @this {any}
407
- * @param {Treelike} treelike
408
- * @param {...any} keys
409
- */
410
- export async function traverse(treelike, ...keys) {
411
- try {
412
- // Await the result here so that, if the path doesn't exist, the catch
413
- // block below will catch the exception.
414
- return await traverseOrThrow.call(this, treelike, ...keys);
415
- } catch (/** @type {any} */ error) {
416
- if (error instanceof TraverseError) {
417
- return undefined;
418
- } else {
419
- throw error;
420
- }
421
- }
422
- }
423
-
424
- /**
425
- * Return the value at the corresponding path of keys. Throw if any interior
426
- * step of the path doesn't lead to a result.
427
- *
428
- * @this {AsyncTree|null|undefined}
429
- * @param {Treelike} treelike
430
- * @param {...any} keys
431
- */
432
- export async function traverseOrThrow(treelike, ...keys) {
433
- // Start our traversal at the root of the tree.
434
- /** @type {any} */
435
- let value = treelike;
436
- let position = 0;
437
-
438
- // If traversal operation was called with a `this` context, use that as the
439
- // target for function calls.
440
- const target = this;
441
-
442
- // Process all the keys.
443
- const remainingKeys = keys.slice();
444
- let key;
445
- while (remainingKeys.length > 0) {
446
- if (value == null) {
447
- throw new TraverseError("A null or undefined value can't be traversed", {
448
- tree: treelike,
449
- keys,
450
- position,
451
- });
452
- }
453
-
454
- // If the value is packed and can be unpacked, unpack it.
455
- if (isUnpackable(value)) {
456
- value = await value.unpack();
457
- }
458
-
459
- if (value instanceof Function) {
460
- // Value is a function: call it with the remaining keys.
461
- const fn = value;
462
- // We'll take as many keys as the function's length, but at least one.
463
- let fnKeyCount = Math.max(fn.length, 1);
464
- const args = remainingKeys.splice(0, fnKeyCount);
465
- key = null;
466
- value = await fn.call(target, ...args);
467
- } else {
468
- // Cast value to a tree.
469
- const tree = from(value);
470
- // Get the next key.
471
- key = remainingKeys.shift();
472
- // Get the value for the key.
473
- value = await tree.get(key);
474
- }
475
-
476
- position++;
477
- }
478
-
479
- return value;
480
- }
481
-
482
- /**
483
- * Given a slash-separated path like "foo/bar", traverse the keys "foo/" and
484
- * "bar" and return the resulting value.
485
- *
486
- * @param {Treelike} tree
487
- * @param {string} path
488
- */
489
- export async function traversePath(tree, path) {
490
- const keys = utilities.keysFromPath(path);
491
- return traverse(tree, ...keys);
492
- }
493
-
494
- /**
495
- * Return the values in the specific node of the tree.
496
- *
497
- * @param {Treelike} treelike
498
- */
499
- export async function values(treelike) {
500
- const tree = from(treelike);
501
- const keys = Array.from(await tree.keys());
502
- const promises = keys.map(async (key) => tree.get(key));
503
- return Promise.all(promises);
504
- }
41
+ export { default as mapExtension } from "./operations/mapExtension.js";
42
+ export { default as mapReduce } from "./operations/mapReduce.js";
43
+ export { default as mask } from "./operations/mask.js";
44
+ export { default as match } from "./operations/match.js";
45
+ export { default as merge } from "./operations/merge.js";
46
+ export { default as paginate } from "./operations/paginate.js";
47
+ export { default as parent } from "./operations/parent.js";
48
+ export { default as paths } from "./operations/paths.js";
49
+ export { default as plain } from "./operations/plain.js";
50
+ export { default as regExpKeys } from "./operations/regExpKeys.js";
51
+ export { default as reverse } from "./operations/reverse.js";
52
+ export { default as root } from "./operations/root.js";
53
+ export { default as scope } from "./operations/scope.js";
54
+ export { default as setDeep } from "./operations/setDeep.js";
55
+ export { default as shuffle } from "./operations/shuffle.js";
56
+ export { default as sort } from "./operations/sort.js";
57
+ export { default as take } from "./operations/take.js";
58
+ export { default as text } from "./operations/text.js";
59
+ export { default as toFunction } from "./operations/toFunction.js";
60
+ export { default as traverse } from "./operations/traverse.js";
61
+ export { default as traverseOrThrow } from "./operations/traverseOrThrow.js";
62
+ export { default as traversePath } from "./operations/traversePath.js";
63
+ export { default as values } from "./operations/values.js";
64
+ export { default as withKeys } from "./operations/withKeys.js";
@@ -0,0 +1,2 @@
1
+ // Names of OS-generated files that should not be enumerated
2
+ export const hiddenFileNames = [".DS_Store"];
@@ -1,11 +1,10 @@
1
- import { Tree } from "../internal.js";
1
+ import { hiddenFileNames } from "../constants.js";
2
+ import assign from "../operations/assign.js";
3
+ import isTreelike from "../operations/isTreelike.js";
2
4
  import * as trailingSlash from "../trailingSlash.js";
3
- import {
4
- hiddenFileNames,
5
- isStringLike,
6
- naturalOrder,
7
- setParent,
8
- } from "../utilities.js";
5
+ import isStringlike from "../utilities/isStringlike.js";
6
+ import naturalOrder from "../utilities/naturalOrder.js";
7
+ import setParent from "../utilities/setParent.js";
9
8
 
10
9
  const TypedArray = Object.getPrototypeOf(Uint8Array);
11
10
 
@@ -145,7 +144,7 @@ export default class BrowserFileTree {
145
144
  value instanceof DataView ||
146
145
  value instanceof Blob;
147
146
 
148
- if (!isWriteable && isStringLike(value)) {
147
+ if (!isWriteable && isStringlike(value)) {
149
148
  // Value has a meaningful `toString` method, use that.
150
149
  value = String(value);
151
150
  isWriteable = true;
@@ -159,13 +158,13 @@ export default class BrowserFileTree {
159
158
  const writable = await fileHandle.createWritable();
160
159
  await writable.write(value);
161
160
  await writable.close();
162
- } else if (Tree.isTreelike(value)) {
161
+ } else if (isTreelike(value)) {
163
162
  // Treat value as a tree and write it out as a subdirectory.
164
163
  const subdirectory = await directory.getDirectoryHandle(baseKey, {
165
164
  create: true,
166
165
  });
167
166
  const destTree = Reflect.construct(this.constructor, [subdirectory]);
168
- await Tree.assign(destTree, value);
167
+ await assign(destTree, value);
169
168
  } else {
170
169
  const typeName = value?.constructor?.name ?? "unknown";
171
170
  throw new TypeError(`Cannot write a value of type ${typeName} as ${key}`);
@@ -1,4 +1,4 @@
1
- import { Tree } from "../internal.js";
1
+ import isAsyncTree from "../operations/isAsyncTree.js";
2
2
  import MapTree from "./MapTree.js";
3
3
 
4
4
  export default class DeepMapTree extends MapTree {
@@ -9,7 +9,7 @@ export default class DeepMapTree extends MapTree {
9
9
  value = Reflect.construct(this.constructor, [value]);
10
10
  }
11
11
 
12
- if (Tree.isAsyncTree(value) && !value.parent) {
12
+ if (isAsyncTree(value) && !value.parent) {
13
13
  value.parent = this;
14
14
  }
15
15
 
@@ -18,6 +18,6 @@ export default class DeepMapTree extends MapTree {
18
18
 
19
19
  /** @returns {boolean} */
20
20
  isSubtree(value) {
21
- return value instanceof Map || Tree.isAsyncTree(value);
21
+ return value instanceof Map || isAsyncTree(value);
22
22
  }
23
23
  }
@@ -1,5 +1,6 @@
1
- import { ObjectTree, Tree } from "../internal.js";
2
- import { isPlainObject } from "../utilities.js";
1
+ import isAsyncTree from "../operations/isAsyncTree.js";
2
+ import isPlainObject from "../utilities/isPlainObject.js";
3
+ import ObjectTree from "./ObjectTree.js";
3
4
 
4
5
  export default class DeepObjectTree extends ObjectTree {
5
6
  async get(key) {
@@ -12,8 +13,6 @@ export default class DeepObjectTree extends ObjectTree {
12
13
 
13
14
  /** @returns {boolean} */
14
15
  isSubtree(value) {
15
- return (
16
- value instanceof Array || isPlainObject(value) || Tree.isAsyncTree(value)
17
- );
16
+ return value instanceof Array || isPlainObject(value) || isAsyncTree(value);
18
17
  }
19
18
  }
@@ -1,4 +1,4 @@
1
- import { Tree } from "../internal.js";
1
+ import from from "../operations/from.js";
2
2
 
3
3
  /**
4
4
  * A tree that is loaded lazily.
@@ -64,7 +64,7 @@ export default class DeferredTree {
64
64
  this.treePromise ??= this.loadResult().then((treelike) => {
65
65
  const options =
66
66
  this._deep !== undefined ? { deep: this._deep } : undefined;
67
- this._tree = Tree.from(treelike, options);
67
+ this._tree = from(treelike, options);
68
68
  if (this._parentUntilLoaded) {
69
69
  // Now that the tree has been loaded, we can set its parent if it hasn't
70
70
  // already been set.