@weborigami/async-tree 0.5.8 → 0.6.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.
- package/browser.js +1 -1
- package/index.ts +43 -35
- package/main.js +1 -2
- package/package.json +4 -7
- package/shared.js +74 -12
- package/src/Tree.js +11 -2
- package/src/drivers/AsyncMap.js +237 -0
- package/src/drivers/{BrowserFileTree.js → BrowserFileMap.js} +54 -38
- package/src/drivers/{calendarTree.js → CalendarMap.js} +80 -63
- package/src/drivers/ConstantMap.js +28 -0
- package/src/drivers/{ExplorableSiteTree.js → ExplorableSiteMap.js} +7 -7
- package/src/drivers/FileMap.js +238 -0
- package/src/drivers/{FunctionTree.js → FunctionMap.js} +19 -22
- package/src/drivers/ObjectMap.js +151 -0
- package/src/drivers/SetMap.js +13 -0
- package/src/drivers/{SiteTree.js → SiteMap.js} +17 -20
- package/src/drivers/SyncMap.js +260 -0
- package/src/jsonKeys.d.ts +2 -2
- package/src/jsonKeys.js +20 -5
- package/src/operations/addNextPrevious.js +35 -36
- package/src/operations/assign.js +27 -23
- package/src/operations/cache.js +30 -36
- package/src/operations/cachedKeyFunctions.js +1 -1
- package/src/operations/calendar.js +5 -0
- package/src/operations/child.js +35 -0
- package/src/operations/clear.js +13 -12
- package/src/operations/constant.js +5 -0
- package/src/operations/deepEntries.js +23 -0
- package/src/operations/deepMap.js +9 -9
- package/src/operations/deepMerge.js +36 -25
- package/src/operations/deepReverse.js +23 -16
- package/src/operations/deepTake.js +7 -7
- package/src/operations/deepText.js +4 -4
- package/src/operations/deepValues.js +3 -6
- package/src/operations/deepValuesIterator.js +11 -11
- package/src/operations/delete.js +8 -12
- package/src/operations/entries.js +17 -10
- package/src/operations/filter.js +9 -7
- package/src/operations/first.js +12 -10
- package/src/operations/forEach.js +10 -13
- package/src/operations/from.js +30 -39
- package/src/operations/globKeys.js +22 -17
- package/src/operations/group.js +2 -2
- package/src/operations/groupBy.js +24 -22
- package/src/operations/has.js +7 -9
- package/src/operations/indent.js +2 -2
- package/src/operations/inners.js +19 -15
- package/src/operations/invokeFunctions.js +22 -10
- package/src/operations/isAsyncMutableTree.js +5 -12
- package/src/operations/isAsyncTree.js +5 -20
- package/src/operations/isMap.js +39 -0
- package/src/operations/isMaplike.js +34 -0
- package/src/operations/isReadOnlyMap.js +14 -0
- package/src/operations/isTraversable.js +3 -3
- package/src/operations/isTreelike.js +5 -30
- package/src/operations/json.js +4 -12
- package/src/operations/keys.js +17 -8
- package/src/operations/length.js +9 -8
- package/src/operations/map.js +28 -30
- package/src/operations/mapExtension.js +20 -16
- package/src/operations/mapReduce.js +38 -24
- package/src/operations/mask.js +54 -29
- package/src/operations/match.js +13 -9
- package/src/operations/merge.js +43 -35
- package/src/operations/paginate.js +26 -18
- package/src/operations/parent.js +7 -7
- package/src/operations/paths.js +20 -22
- package/src/operations/plain.js +6 -6
- package/src/operations/reduce.js +16 -0
- package/src/operations/regExpKeys.js +21 -12
- package/src/operations/reverse.js +21 -15
- package/src/operations/root.js +6 -5
- package/src/operations/scope.js +31 -26
- package/src/operations/set.js +20 -0
- package/src/operations/shuffle.js +23 -16
- package/src/operations/size.js +13 -0
- package/src/operations/sort.js +55 -40
- package/src/operations/sync.js +14 -0
- package/src/operations/take.js +23 -11
- package/src/operations/text.js +4 -4
- package/src/operations/toFunction.js +7 -7
- package/src/operations/traverse.js +4 -4
- package/src/operations/traverseOrThrow.js +18 -9
- package/src/operations/traversePath.js +2 -2
- package/src/operations/values.js +18 -9
- package/src/operations/withKeys.js +22 -16
- package/src/symbols.js +1 -0
- package/src/utilities/castArraylike.js +24 -13
- package/src/utilities/getMapArgument.js +38 -0
- package/src/utilities/getParent.js +2 -2
- package/src/utilities/isStringlike.js +7 -5
- package/src/utilities/setParent.js +7 -7
- package/src/utilities/toFunction.js +2 -2
- package/src/utilities/toPlainValue.js +21 -19
- package/test/SampleAsyncMap.js +34 -0
- package/test/browser/assert.js +20 -0
- package/test/browser/index.html +53 -21
- package/test/drivers/AsyncMap.test.js +119 -0
- package/test/drivers/{BrowserFileTree.test.js → BrowserFileMap.test.js} +50 -33
- package/test/drivers/{calendarTree.test.js → CalendarMap.test.js} +17 -19
- package/test/drivers/ConstantMap.test.js +15 -0
- package/test/drivers/{ExplorableSiteTree.test.js → ExplorableSiteMap.test.js} +29 -14
- package/test/drivers/FileMap.test.js +156 -0
- package/test/drivers/FunctionMap.test.js +56 -0
- package/test/drivers/ObjectMap.test.js +194 -0
- package/test/drivers/SetMap.test.js +35 -0
- package/test/drivers/{SiteTree.test.js → SiteMap.test.js} +14 -10
- package/test/drivers/SyncMap.test.js +335 -0
- package/test/jsonKeys.test.js +18 -6
- package/test/operations/addNextPrevious.test.js +3 -2
- package/test/operations/assign.test.js +30 -35
- package/test/operations/cache.test.js +17 -12
- package/test/operations/cachedKeyFunctions.test.js +12 -9
- package/test/operations/child.test.js +34 -0
- package/test/operations/clear.test.js +6 -27
- package/test/operations/deepEntries.test.js +32 -0
- package/test/operations/deepMerge.test.js +23 -16
- package/test/operations/deepReverse.test.js +2 -2
- package/test/operations/deepTake.test.js +2 -2
- package/test/operations/deepText.test.js +4 -4
- package/test/operations/deepValuesIterator.test.js +2 -2
- package/test/operations/delete.test.js +2 -2
- package/test/operations/extensionKeyFunctions.test.js +6 -5
- package/test/operations/filter.test.js +3 -3
- package/test/operations/from.test.js +25 -31
- package/test/operations/globKeys.test.js +9 -9
- package/test/operations/groupBy.test.js +6 -5
- package/test/operations/inners.test.js +17 -14
- package/test/operations/invokeFunctions.test.js +2 -2
- package/test/operations/isMap.test.js +15 -0
- package/test/operations/isMaplike.test.js +15 -0
- package/test/operations/json.test.js +2 -2
- package/test/operations/keys.test.js +16 -3
- package/test/operations/map.test.js +40 -30
- package/test/operations/mapExtension.test.js +6 -6
- package/test/operations/mapReduce.test.js +14 -12
- package/test/operations/mask.test.js +16 -3
- package/test/operations/match.test.js +2 -2
- package/test/operations/merge.test.js +20 -14
- package/test/operations/paginate.test.js +5 -5
- package/test/operations/parent.test.js +3 -3
- package/test/operations/paths.test.js +20 -27
- package/test/operations/plain.test.js +8 -8
- package/test/operations/regExpKeys.test.js +22 -18
- package/test/operations/reverse.test.js +4 -3
- package/test/operations/scope.test.js +6 -5
- package/test/operations/set.test.js +11 -0
- package/test/operations/shuffle.test.js +3 -2
- package/test/operations/sort.test.js +7 -10
- package/test/operations/sync.test.js +43 -0
- package/test/operations/take.test.js +2 -2
- package/test/operations/toFunction.test.js +2 -2
- package/test/operations/traverse.test.js +17 -5
- package/test/operations/withKeys.test.js +2 -2
- package/test/utilities/castArrayLike.test.js +53 -0
- package/test/utilities/setParent.test.js +6 -6
- package/test/utilities/toFunction.test.js +2 -2
- package/test/utilities/toPlainValue.test.js +51 -12
- package/src/drivers/DeepMapTree.js +0 -23
- package/src/drivers/DeepObjectTree.js +0 -18
- package/src/drivers/DeferredTree.js +0 -81
- package/src/drivers/FileTree.js +0 -276
- package/src/drivers/MapTree.js +0 -70
- package/src/drivers/ObjectTree.js +0 -158
- package/src/drivers/SetTree.js +0 -34
- package/src/drivers/constantTree.js +0 -19
- package/src/drivers/limitConcurrency.js +0 -63
- package/src/internal.js +0 -16
- package/src/utilities/getTreeArgument.js +0 -43
- package/test/drivers/DeepMapTree.test.js +0 -17
- package/test/drivers/DeepObjectTree.test.js +0 -35
- package/test/drivers/DeferredTree.test.js +0 -22
- package/test/drivers/FileTree.test.js +0 -192
- package/test/drivers/FunctionTree.test.js +0 -46
- package/test/drivers/MapTree.test.js +0 -59
- package/test/drivers/ObjectTree.test.js +0 -163
- package/test/drivers/SetTree.test.js +0 -44
- package/test/drivers/constantTree.test.js +0 -13
- package/test/drivers/limitConcurrency.test.js +0 -41
- package/test/operations/isAsyncMutableTree.test.js +0 -17
- package/test/operations/isAsyncTree.test.js +0 -26
- package/test/operations/isTreelike.test.js +0 -13
|
@@ -1,57 +1,56 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import plain from "./plain.js";
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
|
+
import keys from "./keys.js";
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
6
|
+
* Return a map that adds nextKey/previousKey properties to values.
|
|
8
7
|
*
|
|
9
|
-
* @
|
|
10
|
-
* @
|
|
11
|
-
*
|
|
12
|
-
* @param {import("../../index.ts").Treelike} treelike
|
|
13
|
-
* @returns {Promise<PlainObject|Array>}
|
|
8
|
+
* @param {import("../../index.ts").Maplike} maplike
|
|
9
|
+
* @returns {Promise<AsyncMap>}
|
|
14
10
|
*/
|
|
15
|
-
export default async function addNextPrevious(
|
|
16
|
-
const
|
|
11
|
+
export default async function addNextPrevious(maplike) {
|
|
12
|
+
const source = await getMapArgument(maplike, "addNextPrevious");
|
|
13
|
+
let sourceKeys;
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
return Object.assign(new AsyncMap(), {
|
|
16
|
+
async get(key) {
|
|
17
|
+
const sourceValue = await source.get(key);
|
|
18
|
+
if (sourceValue === undefined) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let resultValue;
|
|
26
|
-
if (value === undefined) {
|
|
27
|
-
resultValue = undefined;
|
|
28
|
-
} else if (isTreelike(value)) {
|
|
29
|
-
resultValue = await plain(value);
|
|
30
|
-
} else if (typeof value === "object") {
|
|
31
|
-
// Clone value to avoid modifying the original object
|
|
32
|
-
resultValue = { ...value };
|
|
22
|
+
const resultValue = {};
|
|
23
|
+
if (typeof sourceValue === "object") {
|
|
24
|
+
// Copy to avoid modifying the original object
|
|
25
|
+
Object.assign(resultValue, sourceValue);
|
|
33
26
|
} else {
|
|
34
27
|
// Take the object as the `value` property
|
|
35
|
-
resultValue =
|
|
28
|
+
resultValue.value = sourceValue;
|
|
36
29
|
}
|
|
37
30
|
|
|
38
|
-
|
|
31
|
+
// Find the index of the current key
|
|
32
|
+
sourceKeys ??= await keys(source);
|
|
33
|
+
const index = sourceKeys.indexOf(key);
|
|
34
|
+
if (index >= 0) {
|
|
39
35
|
// Extend result with nextKey/previousKey properties.
|
|
40
|
-
const nextKey =
|
|
36
|
+
const nextKey = sourceKeys[index + 1];
|
|
41
37
|
if (nextKey) {
|
|
42
38
|
resultValue.nextKey = nextKey;
|
|
43
39
|
}
|
|
44
|
-
const previousKey =
|
|
40
|
+
const previousKey = sourceKeys[index - 1];
|
|
45
41
|
if (previousKey) {
|
|
46
42
|
resultValue.previousKey = previousKey;
|
|
47
43
|
}
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
return
|
|
51
|
-
}
|
|
52
|
-
|
|
46
|
+
return resultValue;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
async *keys() {
|
|
50
|
+
sourceKeys ??= await keys(source);
|
|
51
|
+
yield* sourceKeys;
|
|
52
|
+
},
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
: Object.fromEntries(mappedEntries);
|
|
54
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
55
|
+
});
|
|
57
56
|
}
|
package/src/operations/assign.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
|
+
import child from "./child.js";
|
|
3
|
+
import isMaplike from "./isMaplike.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Apply the key/values pairs from the source tree to the target tree.
|
|
@@ -9,32 +9,36 @@ import isAsyncTree from "./isAsyncTree.js";
|
|
|
9
9
|
* subtrees, then the subtrees will be merged recursively. Otherwise, the
|
|
10
10
|
* value from the source tree will overwrite the value in the target tree.
|
|
11
11
|
*
|
|
12
|
-
* @typedef {import("../../index.ts").
|
|
12
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
13
13
|
*
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
14
|
+
* @param {Maplike} target
|
|
15
|
+
* @param {Maplike} source
|
|
16
16
|
*/
|
|
17
17
|
export default async function assign(target, source) {
|
|
18
|
-
const targetTree =
|
|
19
|
-
const sourceTree =
|
|
20
|
-
if (
|
|
21
|
-
throw new TypeError("Target must be a
|
|
18
|
+
const targetTree = await getMapArgument(target, "assign", { position: 0 });
|
|
19
|
+
const sourceTree = await getMapArgument(source, "assign", { position: 1 });
|
|
20
|
+
if ("readOnly" in targetTree && targetTree.readOnly) {
|
|
21
|
+
throw new TypeError("assign: Target must be a read/write map");
|
|
22
22
|
}
|
|
23
|
+
|
|
23
24
|
// Fire off requests to update all keys, then wait for all of them to finish.
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
await assign(
|
|
32
|
-
|
|
25
|
+
const promises = [];
|
|
26
|
+
for await (const key of sourceTree.keys()) {
|
|
27
|
+
const promise = (async () => {
|
|
28
|
+
const sourceValue = await sourceTree.get(key);
|
|
29
|
+
if (isMaplike(sourceValue)) {
|
|
30
|
+
// Recurse to copy subtree
|
|
31
|
+
const targetChild = await child(targetTree, key);
|
|
32
|
+
await assign(targetChild, sourceValue);
|
|
33
|
+
} else {
|
|
34
|
+
// Copy the value from the source to the target.
|
|
35
|
+
await targetTree.set(key, sourceValue);
|
|
33
36
|
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
})();
|
|
38
|
+
promises.push(promise);
|
|
39
|
+
}
|
|
40
|
+
|
|
38
41
|
await Promise.all(promises);
|
|
42
|
+
|
|
39
43
|
return targetTree;
|
|
40
44
|
}
|
package/src/operations/cache.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import SyncMap from "../drivers/SyncMap.js";
|
|
3
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
4
|
+
import child from "./child.js";
|
|
5
|
+
import isMap from "./isMap.js";
|
|
6
|
+
import isReadOnlyMap from "./isReadOnlyMap.js";
|
|
7
|
+
import keys from "./keys.js";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Caches values from a source tree in a second cache tree. Cache source tree
|
|
@@ -10,60 +12,50 @@ import isAsyncTree from "./isAsyncTree.js";
|
|
|
10
12
|
*
|
|
11
13
|
* If no second tree is supplied, an in-memory value cache is used.
|
|
12
14
|
*
|
|
13
|
-
* @typedef {import("
|
|
14
|
-
* @typedef {import("@weborigami/types").AsyncMutableTree} AsyncMutableTree
|
|
15
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
15
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
16
16
|
*
|
|
17
|
-
* @param {
|
|
18
|
-
* @param {
|
|
19
|
-
* @returns {Promise}
|
|
17
|
+
* @param {Maplike} sourceMaplike
|
|
18
|
+
* @param {Maplike} [cacheMaplike]
|
|
19
|
+
* @returns {Promise<SyncMap|AsyncMap>}
|
|
20
20
|
*/
|
|
21
|
-
export default async function treeCache(
|
|
22
|
-
const source = await
|
|
21
|
+
export default async function treeCache(sourceMaplike, cacheMaplike) {
|
|
22
|
+
const source = await getMapArgument(sourceMaplike, "cache", {
|
|
23
23
|
position: 0,
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
/** @type {AsyncMutableTree} */
|
|
27
26
|
let cache;
|
|
28
|
-
if (
|
|
27
|
+
if (cacheMaplike) {
|
|
29
28
|
cache = /** @type {any} */ (
|
|
30
|
-
await
|
|
29
|
+
await getMapArgument(cacheMaplike, "cache", { position: 1 })
|
|
31
30
|
);
|
|
32
31
|
// @ts-ignore
|
|
33
|
-
if (
|
|
34
|
-
throw new Error("Cache tree
|
|
32
|
+
if (isReadOnlyMap(cache)) {
|
|
33
|
+
throw new Error("cache: Cache tree can't be read-only.");
|
|
35
34
|
}
|
|
36
35
|
} else {
|
|
37
|
-
cache = new
|
|
36
|
+
cache = new SyncMap();
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
let
|
|
41
|
-
|
|
39
|
+
let sourceKeys;
|
|
40
|
+
|
|
41
|
+
return Object.assign(new AsyncMap(), {
|
|
42
42
|
description: "cache",
|
|
43
43
|
|
|
44
44
|
async get(key) {
|
|
45
45
|
// Check cache tree first.
|
|
46
46
|
let cacheValue = await cache.get(key);
|
|
47
|
-
if (cacheValue !== undefined && !
|
|
47
|
+
if (cacheValue !== undefined && !isMap(cacheValue)) {
|
|
48
48
|
// Leaf node cache hit
|
|
49
49
|
return cacheValue;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Cache miss or interior node cache hit.
|
|
53
53
|
let value = await source.get(key);
|
|
54
|
-
if (
|
|
54
|
+
if (isMap(value)) {
|
|
55
55
|
// Construct merged tree for a tree result.
|
|
56
56
|
if (cacheValue === undefined) {
|
|
57
57
|
// Construct new empty container in cache
|
|
58
|
-
await cache
|
|
59
|
-
cacheValue = await cache.get(key);
|
|
60
|
-
if (!isAsyncTree(cacheValue)) {
|
|
61
|
-
// Coerce to tree and then save it back to the cache. This is
|
|
62
|
-
// necessary, e.g., if cache is an ObjectTree; we want the
|
|
63
|
-
// subtree to also be an ObjectTree, not a plain object.
|
|
64
|
-
cacheValue = from(cacheValue);
|
|
65
|
-
await cache.set(key, cacheValue);
|
|
66
|
-
}
|
|
58
|
+
cacheValue = await child(cache, key);
|
|
67
59
|
}
|
|
68
60
|
value = treeCache(value, cacheValue);
|
|
69
61
|
} else if (value !== undefined) {
|
|
@@ -74,9 +66,11 @@ export default async function treeCache(sourceTreelike, cacheTreelike) {
|
|
|
74
66
|
return value;
|
|
75
67
|
},
|
|
76
68
|
|
|
77
|
-
async keys() {
|
|
78
|
-
|
|
79
|
-
|
|
69
|
+
async *keys() {
|
|
70
|
+
sourceKeys ??= await keys(source);
|
|
71
|
+
yield* sourceKeys;
|
|
80
72
|
},
|
|
81
|
-
|
|
73
|
+
|
|
74
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
75
|
+
});
|
|
82
76
|
}
|
|
@@ -35,7 +35,7 @@ export default function cachedKeyFunctions(keyFn, deep = false) {
|
|
|
35
35
|
// final match. This is O(n), but we stop as soon as we find a match,
|
|
36
36
|
// and subsequent calls will benefit from the intermediate results.
|
|
37
37
|
const resultKeyWithoutSlash = trailingSlash.remove(resultKey);
|
|
38
|
-
for (const sourceKey of
|
|
38
|
+
for await (const sourceKey of tree.keys()) {
|
|
39
39
|
// Skip any source keys we already know about.
|
|
40
40
|
if (sourceKeyToResultKey.has(sourceKey)) {
|
|
41
41
|
continue;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
|
+
import setParent from "../utilities/setParent.js";
|
|
3
|
+
import isMap from "./isMap.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Return the child node with the indicated key, creating it if necessary.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
9
|
+
*
|
|
10
|
+
* @param {Maplike} maplike
|
|
11
|
+
*/
|
|
12
|
+
export default async function child(maplike, key) {
|
|
13
|
+
const map = await getMapArgument(maplike, "assign", { position: 0 });
|
|
14
|
+
|
|
15
|
+
let result;
|
|
16
|
+
|
|
17
|
+
const hasChildMethod = "child" in map && typeof map.child === "function";
|
|
18
|
+
if (hasChildMethod) {
|
|
19
|
+
// Use tree's own child() method
|
|
20
|
+
result = map.child(key);
|
|
21
|
+
} else {
|
|
22
|
+
// Default implementation
|
|
23
|
+
result = await map.get(key);
|
|
24
|
+
|
|
25
|
+
// If child is already a map we can use it as is
|
|
26
|
+
if (!isMap(result)) {
|
|
27
|
+
// Create new child node using no-arg constructor
|
|
28
|
+
result = new /** @type {any} */ (map).constructor();
|
|
29
|
+
await map.set(key, result);
|
|
30
|
+
setParent(result, map);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return result;
|
|
35
|
+
}
|
package/src/operations/clear.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import isAsyncMutableTree from "./isAsyncMutableTree.js";
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
* Remove all entries from the
|
|
4
|
+
* Remove all entries from the map.
|
|
6
5
|
*
|
|
7
|
-
* @typedef {import("../../index.ts").
|
|
6
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
7
|
*
|
|
9
|
-
* @param {
|
|
8
|
+
* @param {Maplike} maplike
|
|
10
9
|
*/
|
|
11
|
-
export default async function clear(
|
|
12
|
-
const
|
|
13
|
-
if (
|
|
14
|
-
throw new TypeError("clear:
|
|
10
|
+
export default async function clear(maplike) {
|
|
11
|
+
const map = await getMapArgument(maplike, "clear");
|
|
12
|
+
if ("readOnly" in map && map.readOnly) {
|
|
13
|
+
throw new TypeError("clear: target map is read-only");
|
|
14
|
+
}
|
|
15
|
+
const promises = [];
|
|
16
|
+
for await (const key of map.keys()) {
|
|
17
|
+
promises.push(map.delete(key));
|
|
15
18
|
}
|
|
16
|
-
const keys = Array.from(await tree.keys());
|
|
17
|
-
const promises = keys.map((key) => tree.set(key, undefined));
|
|
18
19
|
await Promise.all(promises);
|
|
19
|
-
return
|
|
20
|
+
return map;
|
|
20
21
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
|
+
import entries from "./entries.js";
|
|
3
|
+
import isMap from "./isMap.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Return the deep nested entries in the tree as arrays of [key, value] pairs.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
9
|
+
*
|
|
10
|
+
* @param {Maplike} maplike
|
|
11
|
+
*/
|
|
12
|
+
export default async function deepEntries(maplike) {
|
|
13
|
+
const tree = await getMapArgument(maplike, "deepEntries");
|
|
14
|
+
|
|
15
|
+
const treeEntries = await entries(tree);
|
|
16
|
+
const result = await Promise.all(
|
|
17
|
+
treeEntries.map(async ([key, value]) => {
|
|
18
|
+
const resolvedValue = isMap(value) ? await deepEntries(value) : value;
|
|
19
|
+
return [key, resolvedValue];
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
import isPlainObject from "../utilities/isPlainObject.js";
|
|
3
3
|
import map from "./map.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Shorthand for calling `map` with the `deep: true` option.
|
|
7
7
|
*
|
|
8
|
-
* @typedef {import("../../index.ts").
|
|
9
|
-
* @typedef {import("../../index.ts").
|
|
8
|
+
* @typedef {import("../../index.ts").MapOptions} MapOptions
|
|
9
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
10
10
|
* @typedef {import("../../index.ts").ValueKeyFn} ValueKeyFn
|
|
11
|
-
* @typedef {import("
|
|
11
|
+
* @typedef {import("../../index.ts").AsyncMap} AsyncMap
|
|
12
12
|
*
|
|
13
|
-
* @param {
|
|
14
|
-
* @param {ValueKeyFn|
|
|
15
|
-
* @returns {Promise<
|
|
13
|
+
* @param {Maplike} maplike
|
|
14
|
+
* @param {ValueKeyFn|MapOptions} options
|
|
15
|
+
* @returns {Promise<AsyncMap>}
|
|
16
16
|
*/
|
|
17
|
-
export default async function deepMap(
|
|
18
|
-
const tree = await
|
|
17
|
+
export default async function deepMap(maplike, options) {
|
|
18
|
+
const tree = await getMapArgument(maplike, "deepMap", { deep: true });
|
|
19
19
|
const withDeep = isPlainObject(options)
|
|
20
20
|
? // Dictionary
|
|
21
21
|
{ ...options, deep: true }
|
|
@@ -1,35 +1,42 @@
|
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
1
2
|
import * as trailingSlash from "../trailingSlash.js";
|
|
3
|
+
import isUnpackable from "../utilities/isUnpackable.js";
|
|
2
4
|
import from from "./from.js";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
+
import isMap from "./isMap.js";
|
|
6
|
+
import isMaplike from "./isMaplike.js";
|
|
7
|
+
import keys from "./keys.js";
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Return a tree that performs a deep merge of the given trees.
|
|
8
11
|
*
|
|
9
|
-
* @typedef {import("
|
|
10
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
12
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
11
13
|
*
|
|
12
|
-
* @param {
|
|
13
|
-
* @returns {
|
|
14
|
+
* @param {Maplike[]} maplikes
|
|
15
|
+
* @returns {Promise<AsyncMap>}
|
|
14
16
|
*/
|
|
15
|
-
export default function deepMerge(...
|
|
16
|
-
const filtered =
|
|
17
|
-
|
|
17
|
+
export default async function deepMerge(...maplikes) {
|
|
18
|
+
const filtered = maplikes.filter((source) => source);
|
|
19
|
+
const unpacked = await Promise.all(
|
|
20
|
+
filtered.map(async (source) =>
|
|
21
|
+
isUnpackable(source) ? await source.unpack() : source
|
|
22
|
+
)
|
|
23
|
+
);
|
|
24
|
+
const sources = unpacked.map((maplike) => from(maplike, { deep: true }));
|
|
18
25
|
|
|
19
|
-
return {
|
|
26
|
+
return Object.assign(new AsyncMap(), {
|
|
20
27
|
description: "deepMerge",
|
|
21
28
|
|
|
22
29
|
async get(key) {
|
|
23
30
|
const subtrees = [];
|
|
24
31
|
|
|
25
32
|
// Check trees for the indicated key in reverse order.
|
|
26
|
-
for (let index =
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
) {
|
|
33
|
+
for (let index = sources.length - 1; index >= 0; index--) {
|
|
34
|
+
const source = sources[index];
|
|
35
|
+
const normalized = /** @type {any} */ (source).trailingSlashKeys
|
|
36
|
+
? key
|
|
37
|
+
: trailingSlash.remove(key);
|
|
38
|
+
const value = await source.get(normalized);
|
|
39
|
+
if (isMap(value) || (isMaplike(value) && trailingSlash.has(key))) {
|
|
33
40
|
subtrees.unshift(value);
|
|
34
41
|
} else if (value !== undefined) {
|
|
35
42
|
return value;
|
|
@@ -46,21 +53,25 @@ export default function deepMerge(...sources) {
|
|
|
46
53
|
}
|
|
47
54
|
},
|
|
48
55
|
|
|
49
|
-
async keys() {
|
|
50
|
-
const
|
|
56
|
+
async *keys() {
|
|
57
|
+
const treeKeys = new Set();
|
|
51
58
|
// Collect keys in the order the trees were provided.
|
|
52
|
-
for (const tree of
|
|
53
|
-
for (const key of await
|
|
59
|
+
for (const tree of sources) {
|
|
60
|
+
for (const key of await keys(tree)) {
|
|
54
61
|
// Remove the alternate form of the key (if it exists)
|
|
55
62
|
const alternateKey = trailingSlash.toggle(key);
|
|
56
63
|
if (alternateKey !== key) {
|
|
57
|
-
|
|
64
|
+
treeKeys.delete(alternateKey);
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
|
|
67
|
+
treeKeys.add(key);
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
|
-
|
|
70
|
+
yield* treeKeys;
|
|
64
71
|
},
|
|
65
|
-
|
|
72
|
+
|
|
73
|
+
sources,
|
|
74
|
+
|
|
75
|
+
trailingSlashKeys: true,
|
|
76
|
+
});
|
|
66
77
|
}
|
|
@@ -1,31 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
|
+
import isMap from "./isMap.js";
|
|
4
|
+
import keys from "./keys.js";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Reverse the order of keys at all levels of the tree.
|
|
6
8
|
*
|
|
7
|
-
* @typedef
|
|
8
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
9
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
9
10
|
*
|
|
10
|
-
* @param {
|
|
11
|
-
* @returns {Promise<
|
|
11
|
+
* @param {Maplike} maplike
|
|
12
|
+
* @returns {Promise<AsyncMap>}
|
|
12
13
|
*/
|
|
13
|
-
export default async function deepReverse(
|
|
14
|
-
const
|
|
14
|
+
export default async function deepReverse(maplike) {
|
|
15
|
+
const source = await getMapArgument(maplike, "deepReverse", { deep: true });
|
|
16
|
+
|
|
17
|
+
return Object.assign(new AsyncMap(), {
|
|
18
|
+
description: "deepReverse",
|
|
15
19
|
|
|
16
|
-
return {
|
|
17
20
|
async get(key) {
|
|
18
|
-
let value = await
|
|
19
|
-
if (
|
|
21
|
+
let value = await source.get(key);
|
|
22
|
+
if (isMap(value)) {
|
|
20
23
|
value = deepReverse(value);
|
|
21
24
|
}
|
|
22
25
|
return value;
|
|
23
26
|
},
|
|
24
27
|
|
|
25
|
-
async keys() {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
async *keys() {
|
|
29
|
+
const treeKeys = await keys(source);
|
|
30
|
+
treeKeys.reverse();
|
|
31
|
+
yield* treeKeys;
|
|
29
32
|
},
|
|
30
|
-
|
|
33
|
+
|
|
34
|
+
source,
|
|
35
|
+
|
|
36
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
37
|
+
});
|
|
31
38
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
import from from "./from.js";
|
|
3
|
-
import
|
|
3
|
+
import isMap from "./isMap.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns a function that traverses a tree deeply and returns the values of the
|
|
@@ -9,23 +9,23 @@ import isAsyncTree from "./isAsyncTree.js";
|
|
|
9
9
|
* This is similar to `deepValues`, but it is more efficient for large trees as
|
|
10
10
|
* stops after `count` values.
|
|
11
11
|
*
|
|
12
|
-
* @param {import("../../index.ts").
|
|
12
|
+
* @param {import("../../index.ts").Maplike} maplike
|
|
13
13
|
* @param {number} count
|
|
14
14
|
*/
|
|
15
|
-
export default async function deepTake(
|
|
16
|
-
const tree = await
|
|
15
|
+
export default async function deepTake(maplike, count) {
|
|
16
|
+
const tree = await getMapArgument(maplike, "deepTake", { deep: true });
|
|
17
17
|
const { values } = await traverse(tree, count);
|
|
18
18
|
return from(values, { deep: true });
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
async function traverse(tree, count) {
|
|
22
22
|
const values = [];
|
|
23
|
-
for (const key of
|
|
23
|
+
for await (const key of tree.keys()) {
|
|
24
24
|
if (count <= 0) {
|
|
25
25
|
break;
|
|
26
26
|
}
|
|
27
27
|
let value = await tree.get(key);
|
|
28
|
-
if (
|
|
28
|
+
if (isMap(value)) {
|
|
29
29
|
const traversed = await traverse(value, count);
|
|
30
30
|
values.push(...traversed.values);
|
|
31
31
|
count = traversed.count;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
import toString from "../utilities/toString.js";
|
|
3
3
|
import deepValuesIterator from "./deepValuesIterator.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Concatenate the deep text values in a tree.
|
|
7
7
|
*
|
|
8
|
-
* @param {import("../../index.ts").
|
|
8
|
+
* @param {import("../../index.ts").Maplike} maplike
|
|
9
9
|
*/
|
|
10
|
-
export default async function deepText(
|
|
11
|
-
const tree = await
|
|
10
|
+
export default async function deepText(maplike) {
|
|
11
|
+
const tree = await getMapArgument(maplike, "deepText", { deep: true });
|
|
12
12
|
const strings = [];
|
|
13
13
|
for await (const value of deepValuesIterator(tree, { expand: true })) {
|
|
14
14
|
let string;
|
|
@@ -3,14 +3,11 @@ import deepValuesIterator from "./deepValuesIterator.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Return the in-order exterior values of a tree as a flat array.
|
|
5
5
|
*
|
|
6
|
-
* @param {import("../../index.ts").
|
|
6
|
+
* @param {import("../../index.ts").Maplike} maplike
|
|
7
7
|
* @param {{ expand?: boolean }} [options]
|
|
8
8
|
*/
|
|
9
|
-
export default async function deepValues(
|
|
10
|
-
|
|
11
|
-
options = { expand: false }
|
|
12
|
-
) {
|
|
13
|
-
const iterator = deepValuesIterator(treelike, options);
|
|
9
|
+
export default async function deepValues(maplike, options = { expand: false }) {
|
|
10
|
+
const iterator = deepValuesIterator(maplike, options);
|
|
14
11
|
const values = [];
|
|
15
12
|
for await (const value of iterator) {
|
|
16
13
|
values.push(value);
|