@weborigami/async-tree 0.5.7 → 0.6.0
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 +31 -35
- package/main.js +1 -2
- package/package.json +4 -7
- package/shared.js +77 -12
- package/src/Tree.js +8 -2
- package/src/drivers/AsyncMap.js +210 -0
- package/src/drivers/{BrowserFileTree.js → BrowserFileMap.js} +36 -27
- package/src/drivers/{calendarTree.js → CalendarMap.js} +81 -62
- package/src/drivers/ConstantMap.js +30 -0
- package/src/drivers/DeepObjectMap.js +27 -0
- package/src/drivers/{ExplorableSiteTree.js → ExplorableSiteMap.js} +7 -7
- package/src/drivers/FileMap.js +245 -0
- package/src/drivers/{FunctionTree.js → FunctionMap.js} +19 -22
- package/src/drivers/ObjectMap.js +139 -0
- package/src/drivers/SetMap.js +13 -0
- package/src/drivers/{SiteTree.js → SiteMap.js} +16 -17
- package/src/drivers/SyncMap.js +245 -0
- package/src/jsonKeys.d.ts +2 -2
- package/src/jsonKeys.js +6 -5
- package/src/operations/addNextPrevious.js +35 -36
- package/src/operations/assign.js +30 -21
- package/src/operations/cache.js +29 -35
- package/src/operations/cachedKeyFunctions.js +1 -1
- package/src/operations/calendar.js +5 -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 +31 -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 +27 -30
- package/src/operations/mapExtension.js +20 -16
- package/src/operations/mapReduce.js +22 -17
- package/src/operations/mask.js +31 -22
- 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 +8 -8
- package/src/operations/plain.js +6 -6
- 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/shuffle.js +23 -16
- package/src/operations/size.js +13 -0
- package/src/operations/sort.js +55 -40
- package/src/operations/sync.js +21 -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 +13 -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 +10 -2
- 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 +22 -18
- package/test/SampleAsyncMap.js +34 -0
- package/test/browser/assert.js +20 -0
- package/test/browser/index.html +54 -21
- package/test/drivers/AsyncMap.test.js +119 -0
- package/test/drivers/{BrowserFileTree.test.js → BrowserFileMap.test.js} +42 -23
- package/test/drivers/{calendarTree.test.js → CalendarMap.test.js} +17 -19
- package/test/drivers/ConstantMap.test.js +15 -0
- package/test/drivers/DeepObjectMap.test.js +36 -0
- package/test/drivers/{ExplorableSiteTree.test.js → ExplorableSiteMap.test.js} +29 -14
- package/test/drivers/FileMap.test.js +185 -0
- package/test/drivers/FunctionMap.test.js +56 -0
- package/test/drivers/ObjectMap.test.js +166 -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 +321 -0
- package/test/jsonKeys.test.js +2 -2
- package/test/operations/addNextPrevious.test.js +3 -2
- package/test/operations/assign.test.js +30 -35
- package/test/operations/cache.test.js +8 -6
- package/test/operations/cachedKeyFunctions.test.js +6 -5
- package/test/operations/clear.test.js +6 -27
- package/test/operations/deepEntries.test.js +32 -0
- package/test/operations/deepMerge.test.js +6 -5
- 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 +23 -31
- package/test/operations/globKeys.test.js +9 -9
- package/test/operations/groupBy.test.js +6 -5
- package/test/operations/inners.test.js +4 -4
- 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 +20 -18
- package/test/operations/mapExtension.test.js +6 -6
- package/test/operations/mapReduce.test.js +2 -2
- package/test/operations/mask.test.js +4 -3
- package/test/operations/match.test.js +2 -2
- package/test/operations/merge.test.js +15 -11
- package/test/operations/paginate.test.js +5 -5
- package/test/operations/parent.test.js +3 -3
- package/test/operations/paths.test.js +6 -6
- package/test/operations/plain.test.js +8 -8
- package/test/operations/regExpKeys.test.js +12 -11
- package/test/operations/reverse.test.js +4 -3
- package/test/operations/scope.test.js +6 -5
- 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 +4 -5
- package/test/operations/withKeys.test.js +2 -2
- 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/text.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import toString from "../utilities/toString.js";
|
|
2
2
|
import deepText from "./deepText.js";
|
|
3
|
-
import
|
|
3
|
+
import isMaplike from "./isMaplike.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A tagged template literal function that concatenate the deep text values in
|
|
7
|
-
*
|
|
6
|
+
* A tagged template literal function that concatenate the deep text values in
|
|
7
|
+
* any trees using `deepText`.
|
|
8
8
|
*
|
|
9
9
|
* @param {TemplateStringsArray} strings
|
|
10
10
|
* @param {...any} values
|
|
@@ -13,7 +13,7 @@ export default async function text(strings, ...values) {
|
|
|
13
13
|
// Convert all the values to strings
|
|
14
14
|
const valueTexts = await Promise.all(
|
|
15
15
|
values.map((value) =>
|
|
16
|
-
|
|
16
|
+
isMaplike(value) ? deepText(value) : toString(value)
|
|
17
17
|
)
|
|
18
18
|
);
|
|
19
19
|
// Splice all the strings together
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Returns a function that invokes the
|
|
4
|
+
* Returns a function that invokes the map's `get` method.
|
|
5
5
|
*
|
|
6
|
-
* @typedef {import("../../index.ts").
|
|
6
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
7
7
|
*
|
|
8
|
-
* @param {
|
|
8
|
+
* @param {Maplike} maplike
|
|
9
9
|
* @returns {Promise<Function>}
|
|
10
10
|
*/
|
|
11
|
-
export default async function toFunction(
|
|
12
|
-
const
|
|
13
|
-
return
|
|
11
|
+
export default async function toFunction(maplike) {
|
|
12
|
+
const map = await getMapArgument(maplike, "toFunction");
|
|
13
|
+
return map.get.bind(map);
|
|
14
14
|
}
|
|
@@ -4,16 +4,16 @@ import traverseOrThrow from "./traverseOrThrow.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Return the value at the corresponding path of keys.
|
|
6
6
|
*
|
|
7
|
-
* @typedef {import("../../index.ts").
|
|
7
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
8
8
|
*
|
|
9
|
-
* @param {
|
|
9
|
+
* @param {Maplike} maplike
|
|
10
10
|
* @param {...any} keys
|
|
11
11
|
*/
|
|
12
|
-
export default async function traverse(
|
|
12
|
+
export default async function traverse(maplike, ...keys) {
|
|
13
13
|
try {
|
|
14
14
|
// Await the result here so that, if the path doesn't exist, the catch
|
|
15
15
|
// block below will catch the exception.
|
|
16
|
-
return await traverseOrThrow(
|
|
16
|
+
return await traverseOrThrow(maplike, ...keys);
|
|
17
17
|
} catch (/** @type {any} */ error) {
|
|
18
18
|
if (error instanceof TraverseError) {
|
|
19
19
|
return undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as trailingSlash from "../trailingSlash.js";
|
|
1
2
|
import TraverseError from "../TraverseError.js";
|
|
2
3
|
import isUnpackable from "../utilities/isUnpackable.js";
|
|
3
4
|
import from from "./from.js";
|
|
@@ -6,16 +7,15 @@ import from from "./from.js";
|
|
|
6
7
|
* Return the value at the corresponding path of keys. Throw if any interior
|
|
7
8
|
* step of the path doesn't lead to a result.
|
|
8
9
|
*
|
|
9
|
-
* @typedef {import("../../index.ts").
|
|
10
|
-
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
10
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
11
11
|
*
|
|
12
|
-
* @param {
|
|
12
|
+
* @param {Maplike} maplike
|
|
13
13
|
* @param {...any} keys
|
|
14
14
|
*/
|
|
15
|
-
export default async function traverseOrThrow(
|
|
15
|
+
export default async function traverseOrThrow(maplike, ...keys) {
|
|
16
16
|
// Start our traversal at the root of the tree.
|
|
17
17
|
/** @type {any} */
|
|
18
|
-
let value =
|
|
18
|
+
let value = maplike;
|
|
19
19
|
let position = 0;
|
|
20
20
|
|
|
21
21
|
// Process all the keys.
|
|
@@ -24,7 +24,7 @@ export default async function traverseOrThrow(treelike, ...keys) {
|
|
|
24
24
|
while (remainingKeys.length > 0) {
|
|
25
25
|
if (value == null) {
|
|
26
26
|
throw new TraverseError("A null or undefined value can't be traversed", {
|
|
27
|
-
tree:
|
|
27
|
+
tree: maplike,
|
|
28
28
|
keys,
|
|
29
29
|
position,
|
|
30
30
|
});
|
|
@@ -44,12 +44,16 @@ export default async function traverseOrThrow(treelike, ...keys) {
|
|
|
44
44
|
key = null;
|
|
45
45
|
value = await fn(...args);
|
|
46
46
|
} else {
|
|
47
|
-
// Cast value to a
|
|
48
|
-
const
|
|
47
|
+
// Cast value to a map.
|
|
48
|
+
const map = from(value);
|
|
49
49
|
// Get the next key.
|
|
50
50
|
key = remainingKeys.shift();
|
|
51
|
+
// Remove trailing slash if not supported
|
|
52
|
+
const normalized = /** @type {any} */ (map).trailingSlashKeys
|
|
53
|
+
? key
|
|
54
|
+
: trailingSlash.remove(key);
|
|
51
55
|
// Get the value for the key.
|
|
52
|
-
value = await
|
|
56
|
+
value = await map.get(normalized);
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
position++;
|
|
@@ -5,9 +5,9 @@ import traverse from "./traverse.js";
|
|
|
5
5
|
* Given a slash-separated path like "foo/bar", traverse the keys "foo/" and
|
|
6
6
|
* "bar" and return the resulting value.
|
|
7
7
|
*
|
|
8
|
-
* @typedef {import("../../index.ts").
|
|
8
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
9
9
|
*
|
|
10
|
-
* @param {
|
|
10
|
+
* @param {Maplike} tree
|
|
11
11
|
* @param {string} path
|
|
12
12
|
*/
|
|
13
13
|
export default async function traversePath(tree, path) {
|
package/src/operations/values.js
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Return the values in the
|
|
4
|
+
* Return the values in the map.
|
|
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 values(
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
export default async function values(maplike) {
|
|
11
|
+
const map = await getMapArgument(maplike, "values");
|
|
12
|
+
let result;
|
|
13
|
+
/** @type {any} */
|
|
14
|
+
let iterable = map.values();
|
|
15
|
+
if (Symbol.asyncIterator in iterable) {
|
|
16
|
+
result = [];
|
|
17
|
+
for await (const key of iterable) {
|
|
18
|
+
result.push(key);
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
result = Array.from(iterable);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
15
24
|
}
|
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
import
|
|
1
|
+
import AsyncMap from "../drivers/AsyncMap.js";
|
|
2
|
+
import getMapArgument from "../utilities/getMapArgument.js";
|
|
2
3
|
import values from "./values.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
* Return a
|
|
6
|
+
* Return a map whose keys are provided by the _values_ of a second map (e.g.,
|
|
6
7
|
* an array of keys).
|
|
7
8
|
*
|
|
8
|
-
* @typedef {import("../../index.ts").
|
|
9
|
-
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
9
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
10
10
|
*
|
|
11
|
-
* @param {
|
|
12
|
-
* @param {
|
|
13
|
-
* @returns {Promise<
|
|
11
|
+
* @param {Maplike} maplike
|
|
12
|
+
* @param {Maplike} keysMaplike
|
|
13
|
+
* @returns {Promise<AsyncMap>}
|
|
14
14
|
*/
|
|
15
|
-
export default async function withKeys(
|
|
16
|
-
const
|
|
17
|
-
const
|
|
15
|
+
export default async function withKeys(maplike, keysMaplike) {
|
|
16
|
+
const source = await getMapArgument(maplike, "withKeys", { position: 0 });
|
|
17
|
+
const keysMap = await getMapArgument(keysMaplike, "withKeys", {
|
|
18
18
|
position: 1,
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
let keys;
|
|
22
22
|
|
|
23
|
-
return {
|
|
23
|
+
return Object.assign(new AsyncMap(), {
|
|
24
|
+
description: "withKeys",
|
|
25
|
+
|
|
24
26
|
async get(key) {
|
|
25
|
-
return
|
|
27
|
+
return source.get(key);
|
|
26
28
|
},
|
|
27
29
|
|
|
28
|
-
async keys() {
|
|
29
|
-
keys ??= await values(
|
|
30
|
-
|
|
30
|
+
async *keys() {
|
|
31
|
+
keys ??= await values(keysMap);
|
|
32
|
+
yield* keys;
|
|
31
33
|
},
|
|
32
|
-
|
|
34
|
+
|
|
35
|
+
source: source,
|
|
36
|
+
|
|
37
|
+
trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
|
|
38
|
+
});
|
|
33
39
|
}
|
package/src/symbols.js
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
* @param {any[]} keys
|
|
10
10
|
* @param {any[]} values
|
|
11
11
|
*/
|
|
12
|
-
export default function castArraylike(
|
|
12
|
+
export default function castArraylike(
|
|
13
|
+
keys,
|
|
14
|
+
values,
|
|
15
|
+
createFn = Object.fromEntries
|
|
16
|
+
) {
|
|
13
17
|
if (keys.length === 0) {
|
|
14
18
|
// Empty keys/values means an empty object, not an empty array
|
|
15
19
|
return {};
|
|
@@ -33,6 +37,10 @@ export default function castArraylike(keys, values) {
|
|
|
33
37
|
return values;
|
|
34
38
|
} else {
|
|
35
39
|
// Return a plain object with the (key, value) pairs
|
|
36
|
-
|
|
40
|
+
const entries = [];
|
|
41
|
+
for (let i = 0; i < keys.length; i++) {
|
|
42
|
+
entries.push([keys[i], values[i]]);
|
|
43
|
+
}
|
|
44
|
+
return createFn(entries);
|
|
37
45
|
}
|
|
38
46
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import from from "../operations/from.js";
|
|
2
|
+
import isUnpackable from "./isUnpackable.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Convert the indicated argument to a map, or throw an exception.
|
|
6
|
+
*
|
|
7
|
+
* Tree operations can use this to validate the map argument and provide more
|
|
8
|
+
* helpful error messages. This also unpacks a unpackable map argument.
|
|
9
|
+
*
|
|
10
|
+
* @typedef {import("../../index.ts").AsyncMap} AsyncMap
|
|
11
|
+
* @typedef {import("../../index.ts").Maplike} Maplike
|
|
12
|
+
* @typedef {import("../../index.ts").Unpackable} Unpackable
|
|
13
|
+
*
|
|
14
|
+
* @param {Maplike|Unpackable} maplike
|
|
15
|
+
* @param {string} operation
|
|
16
|
+
* @param {{ deep?: boolean, position?: number }} [options]
|
|
17
|
+
* @returns {Promise<Map|AsyncMap>}
|
|
18
|
+
*/
|
|
19
|
+
export default async function getMapArgument(maplike, operation, options = {}) {
|
|
20
|
+
const deep = options.deep;
|
|
21
|
+
const position = options.position ?? 0;
|
|
22
|
+
|
|
23
|
+
if (isUnpackable(maplike)) {
|
|
24
|
+
maplike = await maplike.unpack();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let map;
|
|
28
|
+
try {
|
|
29
|
+
map = from(maplike, { deep });
|
|
30
|
+
} catch (/** @type {any} */ error) {
|
|
31
|
+
let message = error.message ?? error;
|
|
32
|
+
message = `${operation}: ${message}`;
|
|
33
|
+
const newError = new TypeError(message);
|
|
34
|
+
/** @type {any} */ (newError).position = position;
|
|
35
|
+
throw newError;
|
|
36
|
+
}
|
|
37
|
+
return map;
|
|
38
|
+
}
|
|
@@ -5,11 +5,11 @@ import * as symbols from "../symbols.js";
|
|
|
5
5
|
*
|
|
6
6
|
* This is intended to be called by unpack functions.
|
|
7
7
|
*
|
|
8
|
-
* @typedef {import("
|
|
8
|
+
* @typedef {import("../../index.ts").SyncOrAsyncMap} SyncOrAsyncMap
|
|
9
9
|
*
|
|
10
10
|
* @param {any} packed
|
|
11
11
|
* @param {any} [options]
|
|
12
|
-
* @returns {
|
|
12
|
+
* @returns {SyncOrAsyncMap|null}
|
|
13
13
|
*/
|
|
14
14
|
export default function getParent(packed, options = {}) {
|
|
15
15
|
// Prefer parent set on options
|
|
@@ -6,15 +6,17 @@ import getRealmObjectPrototype from "./getRealmObjectPrototype.js";
|
|
|
6
6
|
*
|
|
7
7
|
* @typedef {import("../../index.ts").Stringlike} Stringlike
|
|
8
8
|
*
|
|
9
|
-
* @param {any}
|
|
9
|
+
* @param {any} object
|
|
10
10
|
* @returns {obj is Stringlike}
|
|
11
11
|
*/
|
|
12
|
-
export default function isStringlike(
|
|
13
|
-
if (typeof
|
|
12
|
+
export default function isStringlike(object) {
|
|
13
|
+
if (typeof object === "string") {
|
|
14
14
|
return true;
|
|
15
|
-
} else if (
|
|
15
|
+
} else if (typeof object === "symbol") {
|
|
16
16
|
return false;
|
|
17
|
-
} else if (
|
|
17
|
+
} else if (object?.toString === undefined) {
|
|
18
|
+
return false;
|
|
19
|
+
} else if (object.toString === getRealmObjectPrototype(object)?.toString) {
|
|
18
20
|
// The stupid Object.prototype.toString implementation always returns
|
|
19
21
|
// "[object Object]", so if that's the only toString method the object has,
|
|
20
22
|
// we return false.
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import isMap from "../operations/isMap.js";
|
|
2
2
|
import * as symbols from "../symbols.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* If the child object doesn't have a parent yet, set it to the indicated
|
|
6
|
-
* parent. If the child is
|
|
6
|
+
* parent. If the child is a map, set the `parent` property. Otherwise,
|
|
7
7
|
* set the `symbols.parent` property.
|
|
8
8
|
*
|
|
9
|
-
* @typedef {import("
|
|
9
|
+
* @typedef {import("../../index.ts").SyncOrAsyncMap} SyncOrAsyncMap
|
|
10
10
|
*
|
|
11
11
|
* @param {*} child
|
|
12
|
-
* @param {
|
|
12
|
+
* @param {SyncOrAsyncMap|null} parent
|
|
13
13
|
*/
|
|
14
14
|
export default function setParent(child, parent) {
|
|
15
|
-
if (
|
|
15
|
+
if (isMap(child)) {
|
|
16
16
|
// Value is a subtree; set its parent to this tree.
|
|
17
|
-
if (!child.parent) {
|
|
18
|
-
child.parent = parent;
|
|
17
|
+
if ("parent" in child && !child.parent) {
|
|
18
|
+
/** @type {any} */ (child).parent = parent;
|
|
19
19
|
}
|
|
20
20
|
} else if (Object.isExtensible(child) && !child[symbols.parent]) {
|
|
21
21
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import from from "../operations/from.js";
|
|
2
|
-
import
|
|
2
|
+
import isMaplike from "../operations/isMaplike.js";
|
|
3
3
|
import isUnpackable from "./isUnpackable.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -29,7 +29,7 @@ export default function toFunction(obj) {
|
|
|
29
29
|
const fn = await fnPromise;
|
|
30
30
|
return fn(...args);
|
|
31
31
|
};
|
|
32
|
-
} else if (
|
|
32
|
+
} else if (isMaplike(obj)) {
|
|
33
33
|
// Return a function that invokes the tree's getter.
|
|
34
34
|
const tree = from(obj);
|
|
35
35
|
return tree.get.bind(tree);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import ObjectMap from "../drivers/ObjectMap.js";
|
|
2
|
+
import isMaplike from "../operations/isMaplike.js";
|
|
3
3
|
import mapReduce from "../operations/mapReduce.js";
|
|
4
4
|
import * as trailingSlash from "../trailingSlash.js";
|
|
5
5
|
import castArraylike from "./castArraylike.js";
|
|
@@ -9,10 +9,8 @@ import toString from "./toString.js";
|
|
|
9
9
|
import TypedArray from "./TypedArray.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Convert the given input to the plainest possible JavaScript value. This
|
|
13
|
-
*
|
|
14
|
-
* CLI, which could a string, a stream of data, or some other kind of JavaScript
|
|
15
|
-
* object.
|
|
12
|
+
* Convert the given input to the plainest possible JavaScript value. This is
|
|
13
|
+
* useful for rendering data for display or serialization.
|
|
16
14
|
*
|
|
17
15
|
* If the input is a function, it will be invoked and its result will be
|
|
18
16
|
* processed.
|
|
@@ -20,7 +18,7 @@ import TypedArray from "./TypedArray.js";
|
|
|
20
18
|
* If the input is a promise, it will be resolved and its result will be
|
|
21
19
|
* processed.
|
|
22
20
|
*
|
|
23
|
-
* If the input is
|
|
21
|
+
* If the input is maplike, it will be converted to a plain JavaScript object,
|
|
24
22
|
* recursively traversing the tree and converting all values to plain types.
|
|
25
23
|
*
|
|
26
24
|
* If the input is stringlike, its text will be returned.
|
|
@@ -33,9 +31,13 @@ import TypedArray from "./TypedArray.js";
|
|
|
33
31
|
* returned as a plain object.
|
|
34
32
|
*
|
|
35
33
|
* @param {any} input
|
|
34
|
+
* @param {import("../../index.ts").ReduceFn} [reduceFn]
|
|
36
35
|
* @returns {Promise<any>}
|
|
37
36
|
*/
|
|
38
|
-
export default async function toPlainValue(
|
|
37
|
+
export default async function toPlainValue(
|
|
38
|
+
input,
|
|
39
|
+
reduceFn = reduceToPlainObject
|
|
40
|
+
) {
|
|
39
41
|
if (input instanceof Function) {
|
|
40
42
|
// Invoke function
|
|
41
43
|
input = input();
|
|
@@ -47,17 +49,9 @@ export default async function toPlainValue(input) {
|
|
|
47
49
|
|
|
48
50
|
if (isPrimitive(input) || input instanceof Date) {
|
|
49
51
|
return input;
|
|
50
|
-
} else if (
|
|
52
|
+
} else if (isMaplike(input)) {
|
|
51
53
|
// Recursively convert tree to plain object.
|
|
52
|
-
return mapReduce(input, toPlainValue
|
|
53
|
-
// Special case for an empty tree: if based on array, return array.
|
|
54
|
-
if (tree instanceof ObjectTree && keys.length === 0) {
|
|
55
|
-
return /** @type {any} */ (tree).object instanceof Array ? [] : {};
|
|
56
|
-
}
|
|
57
|
-
// Normalize slashes in keys.
|
|
58
|
-
keys = keys.map(trailingSlash.remove);
|
|
59
|
-
return castArraylike(keys, values);
|
|
60
|
-
});
|
|
54
|
+
return mapReduce(input, (value) => toPlainValue(value, reduceFn), reduceFn);
|
|
61
55
|
} else if (input instanceof ArrayBuffer || input instanceof TypedArray) {
|
|
62
56
|
// Try to interpret the buffer as UTF-8 text, otherwise use base64.
|
|
63
57
|
const text = toString(input);
|
|
@@ -78,6 +72,16 @@ export default async function toPlainValue(input) {
|
|
|
78
72
|
}
|
|
79
73
|
}
|
|
80
74
|
|
|
75
|
+
function reduceToPlainObject(values, keys, map) {
|
|
76
|
+
// Special case for an empty tree: if based on array, return array.
|
|
77
|
+
if (map instanceof ObjectMap && keys.length === 0) {
|
|
78
|
+
return /** @type {any} */ (map).object instanceof Array ? [] : {};
|
|
79
|
+
}
|
|
80
|
+
// Normalize slashes in keys.
|
|
81
|
+
keys = keys.map(trailingSlash.remove);
|
|
82
|
+
return castArraylike(keys, values);
|
|
83
|
+
}
|
|
84
|
+
|
|
81
85
|
function toBase64(object) {
|
|
82
86
|
if (typeof Buffer !== "undefined") {
|
|
83
87
|
// Node.js environment
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import AsyncMap from "../src/drivers/AsyncMap.js";
|
|
2
|
+
import * as trailingSlash from "../src/trailingSlash.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* For testing AsyncMaps
|
|
6
|
+
*/
|
|
7
|
+
export default class SampleAsyncMap extends AsyncMap {
|
|
8
|
+
constructor(entries) {
|
|
9
|
+
super();
|
|
10
|
+
this.map = new Map(entries);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async delete(key) {
|
|
14
|
+
const normalized = trailingSlash.remove(key);
|
|
15
|
+
return this.map.delete(normalized);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async get(key) {
|
|
19
|
+
let value = this.map.get(key);
|
|
20
|
+
if (value instanceof Array) {
|
|
21
|
+
value = Reflect.construct(this.constructor, [value]);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async *keys() {
|
|
27
|
+
yield* this.map.keys();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async set(key, value) {
|
|
31
|
+
this.map.set(key, value);
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
}
|
package/test/browser/assert.js
CHANGED
|
@@ -44,6 +44,10 @@ assert.deepEqual = (actual, expected) => {
|
|
|
44
44
|
throw new Error(`Expected ${expected} but got ${actual}`);
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
// For browser testing purposes we treat these the same
|
|
48
|
+
assert.strictEqual = assert.equal;
|
|
49
|
+
assert.deepStrictEqual = assert.deepEqual;
|
|
50
|
+
|
|
47
51
|
assert.rejects = async (fn) => {
|
|
48
52
|
try {
|
|
49
53
|
await fn();
|
|
@@ -52,3 +56,19 @@ assert.rejects = async (fn) => {
|
|
|
52
56
|
}
|
|
53
57
|
throw new Error("Expected promise to reject but it resolved");
|
|
54
58
|
};
|
|
59
|
+
|
|
60
|
+
assert.throws = (fn, expected) => {
|
|
61
|
+
try {
|
|
62
|
+
fn();
|
|
63
|
+
} catch (/** @type {any} */ error) {
|
|
64
|
+
if (expected) {
|
|
65
|
+
if (error.name !== expected.name || error.message !== expected.message) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Expected error ${expected.name}: ${expected.message} but got ${error.name}: ${error.message}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
throw new Error("Expected function to throw but it did not");
|
|
74
|
+
};
|
package/test/browser/index.html
CHANGED
|
@@ -11,48 +11,81 @@
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
</script>
|
|
14
|
-
<!-- Omit
|
|
15
|
-
<!-- Omit
|
|
14
|
+
<!-- Omit FileMap.test.js, which is Node.js only -->
|
|
15
|
+
<!-- Omit SiteMap.test.js, which requires mocks -->
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<script type="module" src="../
|
|
20
|
-
<script type="module" src="../drivers/
|
|
21
|
-
<script type="module" src="../drivers/
|
|
22
|
-
<script type="module" src="../drivers/
|
|
23
|
-
<script type="module" src="../drivers/
|
|
24
|
-
<script type="module" src="../drivers/
|
|
25
|
-
<script type="module" src="../drivers/
|
|
26
|
-
<script type="module" src="../drivers/ObjectTree.test.js"></script>
|
|
27
|
-
<script type="module" src="../drivers/SetTree.test.js"></script>
|
|
28
|
-
<script type="module" src="../drivers/calendarTree.test.js"></script>
|
|
17
|
+
<script type="module" src="../drivers/AsyncMap.test.js"></script>
|
|
18
|
+
<script type="module" src="../drivers/BrowserFileMap.test.js"></script>
|
|
19
|
+
<script type="module" src="../drivers/CalendarMap.test.js"></script>
|
|
20
|
+
<script type="module" src="../drivers/ConstantMap.test.js"></script>
|
|
21
|
+
<script type="module" src="../drivers/DeepObjectMap.test.js"></script>
|
|
22
|
+
<script type="module" src="../drivers/FunctionMap.test.js"></script>
|
|
23
|
+
<script type="module" src="../drivers/ObjectMap.test.js"></script>
|
|
24
|
+
<script type="module" src="../drivers/SetMap.test.js"></script>
|
|
25
|
+
<script type="module" src="../drivers/SyncMap.test.js"></script>
|
|
29
26
|
<script type="module" src="../operations/addNextPrevious.test.js"></script>
|
|
27
|
+
<script type="module" src="../operations/assign.test.js"></script>
|
|
30
28
|
<script type="module" src="../operations/cache.test.js"></script>
|
|
31
|
-
<script
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
<script
|
|
30
|
+
type="module"
|
|
31
|
+
src="../operations/cachedKeyFunctions.test.js"
|
|
32
|
+
></script>
|
|
33
|
+
<script type="module" src="../operations/clear.test.js"></script>
|
|
34
|
+
<script type="module" src="../operations/deepEntries.test.js"></script>
|
|
34
35
|
<script type="module" src="../operations/deepMerge.test.js"></script>
|
|
35
36
|
<script type="module" src="../operations/deepReverse.test.js"></script>
|
|
36
37
|
<script type="module" src="../operations/deepTake.test.js"></script>
|
|
37
38
|
<script type="module" src="../operations/deepText.test.js"></script>
|
|
38
39
|
<script type="module" src="../operations/deepValues.test.js"></script>
|
|
39
|
-
<script
|
|
40
|
-
|
|
40
|
+
<script
|
|
41
|
+
type="module"
|
|
42
|
+
src="../operations/deepValuesIterator.test.js"
|
|
43
|
+
></script>
|
|
44
|
+
<script type="module" src="../operations/delete.test.js"></script>
|
|
45
|
+
<script type="module" src="../operations/entries.test.js"></script>
|
|
46
|
+
<script
|
|
47
|
+
type="module"
|
|
48
|
+
src="../operations/extensionKeyFunctions.test.js"
|
|
49
|
+
></script>
|
|
41
50
|
<script type="module" src="../operations/filter.test.js"></script>
|
|
51
|
+
<script type="module" src="../operations/first.test.js"></script>
|
|
52
|
+
<script type="module" src="../operations/forEach.test.js"></script>
|
|
53
|
+
<script type="module" src="../operations/from.test.js"></script>
|
|
42
54
|
<script type="module" src="../operations/globKeys.test.js"></script>
|
|
43
|
-
<script type="module" src="../operations/
|
|
55
|
+
<script type="module" src="../operations/groupBy.test.js"></script>
|
|
56
|
+
<script type="module" src="../operations/groupBy.test.js"></script>
|
|
57
|
+
<script type="module" src="../operations/has.test.js"></script>
|
|
58
|
+
<script type="module" src="../operations/indent.test.js"></script>
|
|
59
|
+
<script type="module" src="../operations/inners.test.js"></script>
|
|
44
60
|
<script type="module" src="../operations/invokeFunctions.test.js"></script>
|
|
61
|
+
<script type="module" src="../operations/isMap.test.js"></script>
|
|
62
|
+
<script type="module" src="../operations/isMaplike.test.js"></script>
|
|
63
|
+
<script type="module" src="../operations/json.test.js"></script>
|
|
64
|
+
<script type="module" src="../operations/keys.test.js"></script>
|
|
65
|
+
<script type="module" src="../operations/length.test.js"></script>
|
|
45
66
|
<script type="module" src="../operations/map.test.js"></script>
|
|
67
|
+
<script type="module" src="../operations/mapExtension.test.js"></script>
|
|
68
|
+
<script type="module" src="../operations/mapReduce.test.js"></script>
|
|
46
69
|
<script type="module" src="../operations/mask.test.js"></script>
|
|
70
|
+
<script type="module" src="../operations/match.test.js"></script>
|
|
47
71
|
<script type="module" src="../operations/merge.test.js"></script>
|
|
48
72
|
<script type="module" src="../operations/paginate.test.js"></script>
|
|
49
73
|
<script type="module" src="../operations/parseExtensions.test.js"></script>
|
|
74
|
+
<script type="module" src="../operations/paths.test.js"></script>
|
|
75
|
+
<script type="module" src="../operations/plain.test.js"></script>
|
|
50
76
|
<script type="module" src="../operations/regExpKeys.test.js"></script>
|
|
51
77
|
<script type="module" src="../operations/reverse.test.js"></script>
|
|
52
78
|
<script type="module" src="../operations/scope.test.js"></script>
|
|
79
|
+
<script type="module" src="../operations/shuffle.test.js"></script>
|
|
53
80
|
<script type="module" src="../operations/sort.test.js"></script>
|
|
81
|
+
<script type="module" src="../operations/sync.test.js"></script>
|
|
54
82
|
<script type="module" src="../operations/take.test.js"></script>
|
|
55
|
-
<script type="module" src="../
|
|
83
|
+
<script type="module" src="../operations/text.test.js"></script>
|
|
84
|
+
<script type="module" src="../operations/toFunction.test.js"></script>
|
|
85
|
+
<script type="module" src="../operations/traverse.test.js"></script>
|
|
86
|
+
<script type="module" src="../operations/traversePath.test.js"></script>
|
|
87
|
+
<script type="module" src="../operations/values.test.js"></script>
|
|
88
|
+
<script type="module" src="../operations/withKeys.test.js"></script>
|
|
56
89
|
</head>
|
|
57
90
|
<body></body>
|
|
58
91
|
</html>
|