@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.
- package/index.ts +16 -6
- package/package.json +2 -2
- package/shared.js +20 -30
- package/src/Tree.js +62 -502
- package/src/constants.js +2 -0
- package/src/drivers/BrowserFileTree.js +9 -10
- package/src/drivers/DeepMapTree.js +3 -3
- package/src/drivers/DeepObjectTree.js +4 -5
- package/src/drivers/DeferredTree.js +2 -2
- package/src/drivers/FileTree.js +11 -33
- package/src/drivers/FunctionTree.js +1 -1
- package/src/drivers/MapTree.js +6 -6
- package/src/drivers/ObjectTree.js +4 -3
- package/src/drivers/SetTree.js +1 -1
- package/src/drivers/SiteTree.js +1 -1
- package/src/drivers/constantTree.js +1 -1
- package/src/extension.js +5 -3
- package/src/jsonKeys.js +5 -7
- package/src/operations/addNextPrevious.js +10 -9
- package/src/operations/assign.js +40 -0
- package/src/operations/cache.js +18 -12
- package/src/operations/cachedKeyFunctions.js +15 -4
- package/src/operations/clear.js +20 -0
- package/src/operations/concat.js +17 -0
- package/src/operations/deepMap.js +25 -0
- package/src/operations/deepMerge.js +11 -25
- package/src/operations/deepReverse.js +6 -7
- package/src/operations/deepTake.js +6 -7
- package/src/operations/deepText.js +4 -4
- package/src/operations/deepValuesIterator.js +8 -6
- package/src/operations/defineds.js +32 -0
- package/src/operations/delete.js +20 -0
- package/src/operations/entries.js +16 -0
- package/src/operations/extensionKeyFunctions.js +1 -1
- package/src/operations/filter.js +7 -8
- package/src/operations/first.js +18 -0
- package/src/operations/forEach.js +20 -0
- package/src/operations/from.js +77 -0
- package/src/operations/fromFn.js +26 -0
- package/src/operations/globKeys.js +8 -8
- package/src/operations/group.js +9 -7
- package/src/operations/has.js +16 -0
- package/src/operations/indent.js +4 -2
- package/src/operations/inners.js +29 -0
- package/src/operations/invokeFunctions.js +5 -4
- package/src/operations/isAsyncMutableTree.js +15 -0
- package/src/operations/isAsyncTree.js +21 -0
- package/src/operations/isTraversable.js +15 -0
- package/src/operations/isTreelike.js +33 -0
- package/src/operations/json.js +4 -3
- package/src/operations/keys.js +14 -0
- package/src/operations/length.js +15 -0
- package/src/operations/map.js +157 -95
- package/src/operations/mapExtension.js +27 -0
- package/src/operations/mapReduce.js +44 -0
- package/src/operations/mask.js +18 -16
- package/src/operations/match.js +74 -0
- package/src/operations/merge.js +22 -20
- package/src/operations/paginate.js +3 -5
- package/src/operations/parent.js +13 -0
- package/src/operations/paths.js +51 -0
- package/src/operations/plain.js +34 -0
- package/src/operations/regExpKeys.js +4 -5
- package/src/operations/remove.js +14 -0
- package/src/operations/reverse.js +4 -6
- package/src/operations/root.js +17 -0
- package/src/operations/scope.js +4 -6
- package/src/operations/setDeep.js +50 -0
- package/src/operations/shuffle.js +46 -0
- package/src/operations/sort.js +19 -12
- package/src/operations/take.js +3 -5
- package/src/operations/text.js +3 -3
- package/src/operations/toFunction.js +14 -0
- package/src/operations/traverse.js +25 -0
- package/src/operations/traverseOrThrow.js +64 -0
- package/src/operations/traversePath.js +16 -0
- package/src/operations/values.js +15 -0
- package/src/operations/withKeys.js +33 -0
- package/src/utilities/TypedArray.js +2 -0
- package/src/utilities/box.js +20 -0
- package/src/utilities/castArraylike.js +38 -0
- package/src/utilities/getParent.js +33 -0
- package/src/utilities/getRealmObjectPrototype.js +19 -0
- package/src/utilities/getTreeArgument.js +43 -0
- package/src/utilities/isPacked.js +20 -0
- package/src/utilities/isPlainObject.js +29 -0
- package/src/utilities/isPrimitive.js +13 -0
- package/src/utilities/isStringlike.js +25 -0
- package/src/utilities/isUnpackable.js +13 -0
- package/src/utilities/keysFromPath.js +34 -0
- package/src/utilities/naturalOrder.js +9 -0
- package/src/utilities/pathFromKeys.js +18 -0
- package/src/utilities/setParent.js +38 -0
- package/src/utilities/toFunction.js +41 -0
- package/src/utilities/toPlainValue.js +95 -0
- package/src/utilities/toString.js +37 -0
- package/test/drivers/ExplorableSiteTree.test.js +1 -1
- package/test/drivers/FileTree.test.js +1 -1
- package/test/drivers/calendarTree.test.js +1 -1
- package/test/jsonKeys.test.js +1 -1
- package/test/operations/assign.test.js +54 -0
- package/test/operations/cache.test.js +1 -1
- package/test/operations/cachedKeyFunctions.test.js +16 -16
- package/test/operations/clear.test.js +34 -0
- package/test/operations/deepMap.test.js +29 -0
- package/test/operations/deepMerge.test.js +2 -6
- package/test/operations/deepReverse.test.js +1 -1
- package/test/operations/defineds.test.js +25 -0
- package/test/operations/delete.test.js +20 -0
- package/test/operations/entries.test.js +18 -0
- package/test/operations/extensionKeyFunctions.test.js +10 -10
- package/test/operations/first.test.js +15 -0
- package/test/operations/fixtures/README.md +1 -0
- package/test/operations/forEach.test.js +22 -0
- package/test/operations/from.test.js +67 -0
- package/test/operations/globKeys.test.js +3 -3
- package/test/operations/has.test.js +15 -0
- package/test/operations/inners.test.js +30 -0
- package/test/operations/invokeFunctions.test.js +1 -1
- package/test/operations/isAsyncMutableTree.test.js +17 -0
- package/test/operations/isAsyncTree.test.js +26 -0
- package/test/operations/isTreelike.test.js +13 -0
- package/test/operations/keys.test.js +15 -0
- package/test/operations/length.test.js +15 -0
- package/test/operations/map.test.js +62 -45
- package/test/operations/mapExtension.test.js +0 -0
- package/test/operations/mapReduce.test.js +23 -0
- package/test/operations/mask.test.js +1 -1
- package/test/operations/match.test.js +33 -0
- package/test/operations/merge.test.js +23 -9
- package/test/operations/paginate.test.js +2 -1
- package/test/operations/parent.test.js +15 -0
- package/test/operations/paths.test.js +40 -0
- package/test/operations/plain.test.js +69 -0
- package/test/operations/reverse.test.js +1 -1
- package/test/operations/scope.test.js +1 -1
- package/test/operations/setDeep.test.js +53 -0
- package/test/operations/shuffle.test.js +18 -0
- package/test/operations/sort.test.js +3 -3
- package/test/operations/toFunction.test.js +16 -0
- package/test/operations/traverse.test.js +43 -0
- package/test/operations/traversePath.test.js +16 -0
- package/test/operations/values.test.js +18 -0
- package/test/operations/withKeys.test.js +21 -0
- package/test/utilities/box.test.js +26 -0
- package/test/utilities/getRealmObjectPrototype.test.js +11 -0
- package/test/utilities/isPlainObject.test.js +13 -0
- package/test/utilities/keysFromPath.test.js +14 -0
- package/test/utilities/naturalOrder.test.js +11 -0
- package/test/utilities/pathFromKeys.test.js +12 -0
- package/test/utilities/setParent.test.js +34 -0
- package/test/utilities/toFunction.test.js +34 -0
- package/test/utilities/toPlainValue.test.js +27 -0
- package/test/utilities/toString.test.js +22 -0
- package/src/Tree.d.ts +0 -24
- package/src/utilities.d.ts +0 -21
- package/src/utilities.js +0 -439
- package/test/Tree.test.js +0 -407
- 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
|
-
*
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
export
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
export
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
export
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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";
|
package/src/constants.js
ADDED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
5
|
-
|
|
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 &&
|
|
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 (
|
|
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
|
|
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
|
|
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 (
|
|
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 ||
|
|
21
|
+
return value instanceof Map || isAsyncTree(value);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
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
|
|
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 =
|
|
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.
|