@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
package/src/operations/match.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import isMap from "./isMap.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Return a tree with the indicated keys (if provided).
|
|
@@ -13,13 +14,12 @@ import isAsyncTree from "./isAsyncTree.js";
|
|
|
13
14
|
* If a key is requested, match against the given pattern and, if matches,
|
|
14
15
|
* invokes the given function with an object containing the matched values.
|
|
15
16
|
*
|
|
16
|
-
* @typedef
|
|
17
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
17
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
18
18
|
* @typedef {import("../../index.ts").Invocable} Invocable
|
|
19
19
|
*
|
|
20
20
|
* @param {string|RegExp} pattern
|
|
21
21
|
* @param {Invocable} resultFn
|
|
22
|
-
* @param {
|
|
22
|
+
* @param {Maplike} [keys]
|
|
23
23
|
*/
|
|
24
24
|
export default function match(pattern, resultFn, keys = []) {
|
|
25
25
|
let regex;
|
|
@@ -36,7 +36,9 @@ export default function match(pattern, resultFn, keys = []) {
|
|
|
36
36
|
throw new Error(`match(): Unsupported pattern`);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const result = {
|
|
39
|
+
const result = Object.assign(new AsyncMap(), {
|
|
40
|
+
description: "match",
|
|
41
|
+
|
|
40
42
|
async get(key) {
|
|
41
43
|
const keyMatch = regex.exec(key);
|
|
42
44
|
if (!keyMatch) {
|
|
@@ -45,7 +47,7 @@ export default function match(pattern, resultFn, keys = []) {
|
|
|
45
47
|
|
|
46
48
|
if (
|
|
47
49
|
typeof resultFn !== "function" &&
|
|
48
|
-
!(
|
|
50
|
+
!(isMap(resultFn) && "parent" in resultFn)
|
|
49
51
|
) {
|
|
50
52
|
// Simple return value; return as is
|
|
51
53
|
return resultFn;
|
|
@@ -65,10 +67,12 @@ export default function match(pattern, resultFn, keys = []) {
|
|
|
65
67
|
return value;
|
|
66
68
|
},
|
|
67
69
|
|
|
68
|
-
async keys() {
|
|
69
|
-
|
|
70
|
+
async *keys() {
|
|
71
|
+
yield* typeof keys === "function" ? await keys() : keys;
|
|
70
72
|
},
|
|
71
|
-
|
|
73
|
+
|
|
74
|
+
trailingSlashKeys: false,
|
|
75
|
+
});
|
|
72
76
|
|
|
73
77
|
return result;
|
|
74
78
|
}
|
package/src/operations/merge.js
CHANGED
|
@@ -1,51 +1,59 @@
|
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
1
2
|
import * as trailingSlash from "../trailingSlash.js";
|
|
2
3
|
import isPlainObject from "../utilities/isPlainObject.js";
|
|
4
|
+
import isUnpackable from "../utilities/isUnpackable.js";
|
|
3
5
|
import from from "./from.js";
|
|
4
|
-
import
|
|
6
|
+
import isMap from "./isMap.js";
|
|
7
|
+
import keys from "./keys.js";
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Return a tree that performs a shallow merge of the given trees.
|
|
8
11
|
*
|
|
9
|
-
* This is similar to an object spread in JavaScript extended to
|
|
10
|
-
* Given a set of trees, the `get` method looks at each tree in turn,
|
|
11
|
-
* from the *last* tree and working backwards to the first. If a tree
|
|
12
|
-
* defined value for the key, that value is returned. If none of the
|
|
13
|
-
* return a defined value, the `get` method returns undefined.
|
|
12
|
+
* This is similar to an object spread in JavaScript extended to asynchronous
|
|
13
|
+
* trees. Given a set of trees, the `get` method looks at each tree in turn,
|
|
14
|
+
* starting from the *last* tree and working backwards to the first. If a tree
|
|
15
|
+
* returns a defined value for the key, that value is returned. If none of the
|
|
16
|
+
* trees return a defined value, the `get` method returns undefined.
|
|
14
17
|
*
|
|
15
|
-
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
16
18
|
* @typedef {import("../../index.ts").PlainObject} PlainObject
|
|
17
|
-
* @typedef {import("../../index.ts").
|
|
19
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
18
20
|
*
|
|
19
|
-
* @param {
|
|
20
|
-
* @returns {
|
|
21
|
+
* @param {Maplike[]} treelikes
|
|
22
|
+
* @returns {Promise}
|
|
21
23
|
*/
|
|
22
|
-
export default function merge(...
|
|
23
|
-
const filtered =
|
|
24
|
+
export default async function merge(...treelikes) {
|
|
25
|
+
const filtered = treelikes.filter((source) => source);
|
|
26
|
+
const unpacked = await Promise.all(
|
|
27
|
+
filtered.map(async (source) =>
|
|
28
|
+
isUnpackable(source) ? await source.unpack() : source
|
|
29
|
+
)
|
|
30
|
+
);
|
|
24
31
|
|
|
25
32
|
// If all arguments are plain objects, return a plain object.
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
) {
|
|
29
|
-
return filtered.reduce((acc, obj) => ({ ...acc, ...obj }), {});
|
|
33
|
+
if (unpacked.every((source) => !isMap(source) && isPlainObject(source))) {
|
|
34
|
+
return unpacked.reduce((acc, obj) => ({ ...acc, ...obj }), {});
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
const
|
|
37
|
+
const sources = unpacked.map((maplike) => from(maplike));
|
|
33
38
|
|
|
34
|
-
if (
|
|
39
|
+
if (sources.length === 0) {
|
|
35
40
|
throw new TypeError("merge: all trees are null or undefined");
|
|
36
|
-
} else if (
|
|
41
|
+
} else if (sources.length === 1) {
|
|
37
42
|
// Only one tree, no need to merge
|
|
38
|
-
return
|
|
43
|
+
return sources[0];
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
return {
|
|
46
|
+
return Object.assign(new AsyncMap(), {
|
|
42
47
|
description: "merge",
|
|
43
48
|
|
|
44
49
|
async get(key) {
|
|
45
50
|
// Check trees for the indicated key in reverse order.
|
|
46
|
-
for (let index =
|
|
47
|
-
const
|
|
48
|
-
const
|
|
51
|
+
for (let index = sources.length - 1; index >= 0; index--) {
|
|
52
|
+
const source = sources[index];
|
|
53
|
+
const normalized = /** @type {any} */ (source).trailingSlashKeys
|
|
54
|
+
? key
|
|
55
|
+
: trailingSlash.remove(key);
|
|
56
|
+
const value = await source.get(normalized);
|
|
49
57
|
if (value !== undefined) {
|
|
50
58
|
return value;
|
|
51
59
|
}
|
|
@@ -53,25 +61,25 @@ export default function merge(...sources) {
|
|
|
53
61
|
return undefined;
|
|
54
62
|
},
|
|
55
63
|
|
|
56
|
-
async keys() {
|
|
57
|
-
const
|
|
64
|
+
async *keys() {
|
|
65
|
+
const treeKeys = new Set();
|
|
58
66
|
// Collect keys in the order the trees were provided.
|
|
59
|
-
for (const tree of
|
|
60
|
-
for (const key of await
|
|
67
|
+
for (const tree of sources) {
|
|
68
|
+
for (const key of await keys(tree)) {
|
|
61
69
|
// Remove the alternate form of the key (if it exists)
|
|
62
70
|
const alternateKey = trailingSlash.toggle(key);
|
|
63
71
|
if (alternateKey !== key) {
|
|
64
|
-
|
|
72
|
+
treeKeys.delete(alternateKey);
|
|
65
73
|
}
|
|
66
74
|
|
|
67
|
-
|
|
75
|
+
treeKeys.add(key);
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
|
-
|
|
78
|
+
yield* treeKeys;
|
|
71
79
|
},
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
81
|
+
sources,
|
|
82
|
+
|
|
83
|
+
trailingSlashKeys: true,
|
|
84
|
+
});
|
|
77
85
|
}
|
|
@@ -1,23 +1,27 @@
|
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import SyncMap from "../drivers/SyncMap.js";
|
|
1
3
|
import * as trailingSlash from "../trailingSlash.js";
|
|
2
|
-
import
|
|
4
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
5
|
+
import keys from "./keys.js";
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
|
-
* Return a new grouping of the
|
|
8
|
+
* Return a new grouping of the map's values into chunks of the specified
|
|
6
9
|
* size.
|
|
7
10
|
*
|
|
8
|
-
* @typedef {import("
|
|
9
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
11
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
10
12
|
*
|
|
11
|
-
* @param {
|
|
13
|
+
* @param {Maplike} maplike
|
|
12
14
|
* @param {number} [size=10]
|
|
13
15
|
*/
|
|
14
|
-
export default async function paginate(
|
|
15
|
-
const
|
|
16
|
+
export default async function paginate(maplike, size = 10) {
|
|
17
|
+
const source = await getMapArgument(maplike, "paginate");
|
|
16
18
|
|
|
17
|
-
const
|
|
18
|
-
const pageCount = Math.ceil(
|
|
19
|
+
const treeKeys = await keys(source);
|
|
20
|
+
const pageCount = Math.ceil(treeKeys.length / size);
|
|
21
|
+
|
|
22
|
+
const paginated = Object.assign(new AsyncMap(), {
|
|
23
|
+
description: "paginate",
|
|
19
24
|
|
|
20
|
-
const paginated = {
|
|
21
25
|
async get(pageKey) {
|
|
22
26
|
const normalized = trailingSlash.remove(pageKey);
|
|
23
27
|
// Note: page numbers are 1-based.
|
|
@@ -27,14 +31,14 @@ export default async function paginate(treelike, size = 10) {
|
|
|
27
31
|
}
|
|
28
32
|
const nextPage = pageNumber + 1 <= pageCount ? pageNumber + 1 : null;
|
|
29
33
|
const previousPage = pageNumber - 1 >= 1 ? pageNumber - 1 : null;
|
|
30
|
-
const items = new
|
|
34
|
+
const items = new SyncMap();
|
|
31
35
|
for (
|
|
32
36
|
let index = (pageNumber - 1) * size;
|
|
33
|
-
index < Math.min(
|
|
37
|
+
index < Math.min(treeKeys.length, pageNumber * size);
|
|
34
38
|
index++
|
|
35
39
|
) {
|
|
36
|
-
const key =
|
|
37
|
-
items.set(key, await
|
|
40
|
+
const key = treeKeys[index];
|
|
41
|
+
items.set(key, await source.get(treeKeys[index]));
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
return {
|
|
@@ -46,11 +50,15 @@ export default async function paginate(treelike, size = 10) {
|
|
|
46
50
|
};
|
|
47
51
|
},
|
|
48
52
|
|
|
49
|
-
async keys() {
|
|
50
|
-
// Return
|
|
51
|
-
|
|
53
|
+
async *keys() {
|
|
54
|
+
// Return from 1..totalPages
|
|
55
|
+
yield* Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
52
56
|
},
|
|
53
|
-
|
|
57
|
+
|
|
58
|
+
source: source,
|
|
59
|
+
|
|
60
|
+
trailingSlashKeys: false,
|
|
61
|
+
});
|
|
54
62
|
|
|
55
63
|
return paginated;
|
|
56
64
|
}
|
package/src/operations/parent.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Returns the parent of the
|
|
4
|
+
* Returns the parent of the map, if any.
|
|
5
5
|
*
|
|
6
|
-
* @typedef {import("../../index.ts").
|
|
6
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
7
7
|
*
|
|
8
|
-
* @param {
|
|
8
|
+
* @param {Maplike} maplike
|
|
9
9
|
*/
|
|
10
|
-
export default async function parent(
|
|
11
|
-
const
|
|
12
|
-
return
|
|
10
|
+
export default async function parent(maplike) {
|
|
11
|
+
const map = await getMapArgument(maplike, "parent");
|
|
12
|
+
return "parent" in map ? map.parent : undefined;
|
|
13
13
|
}
|
package/src/operations/paths.js
CHANGED
|
@@ -1,47 +1,45 @@
|
|
|
1
1
|
import * as trailingSlash from "../trailingSlash.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
3
|
import from from "./from.js";
|
|
3
|
-
import
|
|
4
|
+
import isMap from "./isMap.js";
|
|
5
|
+
import isMaplike from "./isMaplike.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Returns slash-separated paths for all values in the tree.
|
|
7
9
|
*
|
|
8
10
|
* The `base` argument is prepended to all paths.
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
* indicate subtrees. The default value of this option is false.
|
|
12
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
12
13
|
*
|
|
13
|
-
* @
|
|
14
|
-
*
|
|
15
|
-
* @param {Treelike} treelike
|
|
16
|
-
* @param {{ assumeSlashes?: boolean, base?: string }} options
|
|
14
|
+
* @param {Maplike} maplike
|
|
15
|
+
* @param {{ base?: string }} options
|
|
17
16
|
*/
|
|
18
|
-
export default async function paths(
|
|
19
|
-
const tree =
|
|
17
|
+
export default async function paths(maplike, options = {}) {
|
|
18
|
+
const tree = await getMapArgument(maplike, "paths");
|
|
20
19
|
const base = options.base ?? "";
|
|
21
|
-
const assumeSlashes = options.assumeSlashes ?? false;
|
|
22
20
|
const result = [];
|
|
23
|
-
for (const key of
|
|
21
|
+
for await (const key of tree.keys()) {
|
|
24
22
|
const separator = trailingSlash.has(base) ? "" : "/";
|
|
25
23
|
const valuePath = base ? `${base}${separator}${key}` : key;
|
|
26
|
-
let isSubtree;
|
|
27
24
|
let value;
|
|
28
|
-
if (
|
|
25
|
+
if (/** @type {any} */ (tree).trailingSlashKeys) {
|
|
29
26
|
// Subtree needs to have a trailing slash
|
|
30
|
-
|
|
31
|
-
if (isSubtree) {
|
|
27
|
+
if (trailingSlash.has(key)) {
|
|
32
28
|
// We'll need the value to recurse
|
|
33
29
|
value = await tree.get(key);
|
|
30
|
+
// If it's maplike, treat as subtree
|
|
31
|
+
if (isMaplike(value)) {
|
|
32
|
+
value = from(value);
|
|
33
|
+
}
|
|
34
34
|
}
|
|
35
35
|
} else {
|
|
36
|
-
// Get value
|
|
36
|
+
// Get value
|
|
37
37
|
value = await tree.get(key);
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (isSubtree) {
|
|
44
|
-
const subPaths = await paths(value, { assumeSlashes, base: valuePath });
|
|
39
|
+
|
|
40
|
+
if (isMap(value)) {
|
|
41
|
+
// Subtree; recurse
|
|
42
|
+
const subPaths = await paths(value, { base: valuePath });
|
|
45
43
|
result.push(...subPaths);
|
|
46
44
|
} else {
|
|
47
45
|
result.push(valuePath);
|
package/src/operations/plain.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
import toPlainValue from "../utilities/toPlainValue.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -20,15 +20,15 @@ import toPlainValue from "../utilities/toPlainValue.js";
|
|
|
20
20
|
* array `["b", "a", "c" ]` because the tree has the keys in that order. The
|
|
21
21
|
* specific values of the keys (0, 1, and 2) are ignored.
|
|
22
22
|
*
|
|
23
|
-
* @typedef {import("../../index.ts").
|
|
23
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
24
24
|
* @typedef {import("../../index.ts").PlainObject} PlainObject
|
|
25
25
|
*
|
|
26
|
-
* @param {
|
|
26
|
+
* @param {Maplike} maplike
|
|
27
27
|
*/
|
|
28
|
-
export default async function plain(
|
|
29
|
-
if (
|
|
28
|
+
export default async function plain(maplike) {
|
|
29
|
+
if (maplike instanceof Function) {
|
|
30
30
|
throw new TypeError("plain: can't convert a function to a plain object");
|
|
31
31
|
}
|
|
32
|
-
const tree = await
|
|
32
|
+
const tree = await getMapArgument(maplike, "plain");
|
|
33
33
|
return toPlainValue(tree);
|
|
34
34
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
|
+
import mapReduce from "./mapReduce.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reduce a tree by recursively applying a reducer function to its nodes.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
|
+
* @typedef {import("../../index.ts").ReduceFn} ReduceFn
|
|
9
|
+
*
|
|
10
|
+
* @param {Maplike} maplike
|
|
11
|
+
* @param {ReduceFn} reduceFn
|
|
12
|
+
*/
|
|
13
|
+
export default async function reduce(maplike, reduceFn) {
|
|
14
|
+
const map = await getMapArgument(maplike, "reduce");
|
|
15
|
+
return mapReduce(map, null, reduceFn);
|
|
16
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import SyncMap from "../drivers/SyncMap.js";
|
|
1
3
|
import * as trailingSlash from "../trailingSlash.js";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
5
|
+
import isMap from "./isMap.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* A tree whose keys are strings interpreted as regular expressions.
|
|
@@ -10,16 +12,19 @@ import isAsyncTree from "./isAsyncTree.js";
|
|
|
10
12
|
* taken to match the entire key -- if they do not already start and end with
|
|
11
13
|
* `^` and `$` respectively, those are added.
|
|
12
14
|
*
|
|
13
|
-
* @
|
|
15
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
16
|
+
*
|
|
17
|
+
* @param {Maplike} maplike
|
|
18
|
+
* @returns {Promise<AsyncMap>}
|
|
14
19
|
*/
|
|
15
|
-
export default async function regExpKeys(
|
|
16
|
-
const
|
|
20
|
+
export default async function regExpKeys(maplike) {
|
|
21
|
+
const source = await getMapArgument(maplike, "regExpKeys", { deep: true });
|
|
17
22
|
|
|
18
|
-
const map = new
|
|
23
|
+
const map = new SyncMap();
|
|
19
24
|
|
|
20
25
|
// We build the output tree first so that we can refer to it when setting
|
|
21
26
|
// `parent` on subtrees below.
|
|
22
|
-
let result = {
|
|
27
|
+
let result = Object.assign(new AsyncMap(), {
|
|
23
28
|
// @ts-ignore
|
|
24
29
|
description: "regExpKeys",
|
|
25
30
|
|
|
@@ -39,24 +44,28 @@ export default async function regExpKeys(treelike) {
|
|
|
39
44
|
return undefined;
|
|
40
45
|
},
|
|
41
46
|
|
|
42
|
-
async keys() {
|
|
47
|
+
async *keys() {
|
|
43
48
|
return map.keys();
|
|
44
49
|
},
|
|
45
|
-
|
|
50
|
+
|
|
51
|
+
source: source,
|
|
52
|
+
|
|
53
|
+
trailingSlashKeys: false,
|
|
54
|
+
});
|
|
46
55
|
|
|
47
56
|
// Turn the input tree's string keys into regular expressions, then map those
|
|
48
57
|
// to the corresponding values.
|
|
49
|
-
for (const key of
|
|
58
|
+
for await (const key of source.keys()) {
|
|
50
59
|
if (typeof key !== "string") {
|
|
51
60
|
// Skip non-string keys.
|
|
52
61
|
continue;
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
// Get value.
|
|
56
|
-
let value = await
|
|
65
|
+
let value = await source.get(key);
|
|
57
66
|
|
|
58
67
|
let regExp;
|
|
59
|
-
if (trailingSlash.has(key) ||
|
|
68
|
+
if (trailingSlash.has(key) || isMap(value)) {
|
|
60
69
|
const baseKey = trailingSlash.remove(key);
|
|
61
70
|
regExp = new RegExp("^" + baseKey + "/?$");
|
|
62
71
|
// Subtree
|
|
@@ -1,26 +1,32 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
|
+
import keys from "./keys.js";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
6
|
+
* Return a new map with the keys reversed.
|
|
5
7
|
*
|
|
6
|
-
* @typedef
|
|
7
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
9
|
*
|
|
9
|
-
* @param {
|
|
10
|
-
* @returns {Promise<
|
|
10
|
+
* @param {Maplike} maplike
|
|
11
|
+
* @returns {Promise<AsyncMap>}
|
|
11
12
|
*/
|
|
12
|
-
export default async function reverse(
|
|
13
|
-
const
|
|
13
|
+
export default async function reverse(maplike) {
|
|
14
|
+
const source = await getMapArgument(maplike, "reverse");
|
|
15
|
+
return Object.assign(new AsyncMap(), {
|
|
16
|
+
description: "reverse",
|
|
14
17
|
|
|
15
|
-
return {
|
|
16
18
|
async get(key) {
|
|
17
|
-
return
|
|
19
|
+
return source.get(key);
|
|
18
20
|
},
|
|
19
21
|
|
|
20
|
-
async keys() {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
async *keys() {
|
|
23
|
+
const treeKeys = await keys(source);
|
|
24
|
+
treeKeys.reverse();
|
|
25
|
+
yield* treeKeys;
|
|
24
26
|
},
|
|
25
|
-
|
|
27
|
+
|
|
28
|
+
source,
|
|
29
|
+
|
|
30
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
31
|
+
});
|
|
26
32
|
}
|
package/src/operations/root.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import * as symbols from "../symbols.js";
|
|
2
|
-
import
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Walk up the `parent` chain to find the root of the tree.
|
|
6
6
|
*
|
|
7
|
-
* @typedef {import("../../index.ts").
|
|
7
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
8
|
*
|
|
9
|
-
* @param {
|
|
9
|
+
* @param {Maplike} maplike
|
|
10
10
|
*/
|
|
11
|
-
export default function root(
|
|
12
|
-
|
|
11
|
+
export default function root(maplike) {
|
|
12
|
+
/** @type {any} */
|
|
13
|
+
let current = getMapArgument(maplike, "root");
|
|
13
14
|
while (current.parent || current[symbols.parent]) {
|
|
14
15
|
current = current.parent || current[symbols.parent];
|
|
15
16
|
}
|
package/src/operations/scope.js
CHANGED
|
@@ -1,48 +1,49 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
* A
|
|
5
|
+
* A map's "scope" is the collection of everything in that map and all of its
|
|
5
6
|
* ancestors.
|
|
6
7
|
*
|
|
7
|
-
* @typedef {import("
|
|
8
|
-
* @typedef {import("../../index.ts").Treelike} Treelike
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
9
9
|
*
|
|
10
|
-
* @param {
|
|
11
|
-
* @returns {Promise<
|
|
10
|
+
* @param {Maplike} maplike
|
|
11
|
+
* @returns {Promise<AsyncMap>}
|
|
12
12
|
*/
|
|
13
|
-
export default async function scope(
|
|
14
|
-
const
|
|
13
|
+
export default async function scope(maplike) {
|
|
14
|
+
const source = await getMapArgument(maplike, "scope");
|
|
15
15
|
|
|
16
|
-
return {
|
|
17
|
-
|
|
16
|
+
return Object.assign(new AsyncMap(), {
|
|
17
|
+
description: "scope",
|
|
18
|
+
|
|
19
|
+
// Starting with this map, search up the parent hierarchy.
|
|
18
20
|
async get(key) {
|
|
19
|
-
/** @type {
|
|
20
|
-
let current =
|
|
21
|
+
/** @type {Map|AsyncMap|null} */
|
|
22
|
+
let current = source;
|
|
21
23
|
let value;
|
|
22
24
|
while (current) {
|
|
23
25
|
value = await current.get(key);
|
|
24
26
|
if (value !== undefined) {
|
|
25
27
|
break;
|
|
26
28
|
}
|
|
27
|
-
current = current.parent;
|
|
29
|
+
current = "parent" in current ? current.parent : null;
|
|
28
30
|
}
|
|
29
31
|
return value;
|
|
30
32
|
},
|
|
31
33
|
|
|
32
34
|
// Collect all keys for this tree and all parents
|
|
33
|
-
async keys() {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let current = tree;
|
|
35
|
+
async *keys() {
|
|
36
|
+
const scopeKeys = new Set();
|
|
37
|
+
/** @type {Map|AsyncMap|null} */
|
|
38
|
+
let current = source;
|
|
38
39
|
while (current) {
|
|
39
|
-
for (const key of
|
|
40
|
-
|
|
40
|
+
for await (const key of current.keys()) {
|
|
41
|
+
scopeKeys.add(key);
|
|
41
42
|
}
|
|
42
|
-
current = current.parent;
|
|
43
|
+
current = "parent" in current ? current.parent : null;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
yield* scopeKeys;
|
|
46
47
|
},
|
|
47
48
|
|
|
48
49
|
// Collect all keys for this tree and all parents.
|
|
@@ -52,14 +53,18 @@ export default async function scope(treelike) {
|
|
|
52
53
|
get trees() {
|
|
53
54
|
const result = [];
|
|
54
55
|
|
|
55
|
-
/** @type {
|
|
56
|
-
let current =
|
|
56
|
+
/** @type {Map|AsyncMap|null} */
|
|
57
|
+
let current = source;
|
|
57
58
|
while (current) {
|
|
58
59
|
result.push(current);
|
|
59
|
-
current = current.parent;
|
|
60
|
+
current = "parent" in current ? current.parent : null;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
return result;
|
|
63
64
|
},
|
|
64
|
-
|
|
65
|
+
|
|
66
|
+
source: source,
|
|
67
|
+
|
|
68
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
69
|
+
});
|
|
65
70
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Set a key/value pair in a map.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
7
|
+
*
|
|
8
|
+
* @param {Maplike} maplike
|
|
9
|
+
* @param {any} key
|
|
10
|
+
* @param {any} value
|
|
11
|
+
*/
|
|
12
|
+
export default async function set(maplike, key, value) {
|
|
13
|
+
const map = await getMapArgument(maplike, "set");
|
|
14
|
+
await map.set(key, value);
|
|
15
|
+
|
|
16
|
+
// Unlike Map.prototype.set, we return undefined. This is more useful when
|
|
17
|
+
// calling set in the console -- return the complete tree would result in it
|
|
18
|
+
// being dumped to the console.
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|