@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,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,7 +1,6 @@
|
|
|
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
|
-
import * as trailingSlash from "../trailingSlash.js";
|
|
5
4
|
import castArraylike from "./castArraylike.js";
|
|
6
5
|
import isPrimitive from "./isPrimitive.js";
|
|
7
6
|
import isStringlike from "./isStringlike.js";
|
|
@@ -9,10 +8,8 @@ import toString from "./toString.js";
|
|
|
9
8
|
import TypedArray from "./TypedArray.js";
|
|
10
9
|
|
|
11
10
|
/**
|
|
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.
|
|
11
|
+
* Convert the given input to the plainest possible JavaScript value. This is
|
|
12
|
+
* useful for rendering data for display or serialization.
|
|
16
13
|
*
|
|
17
14
|
* If the input is a function, it will be invoked and its result will be
|
|
18
15
|
* processed.
|
|
@@ -20,7 +17,7 @@ import TypedArray from "./TypedArray.js";
|
|
|
20
17
|
* If the input is a promise, it will be resolved and its result will be
|
|
21
18
|
* processed.
|
|
22
19
|
*
|
|
23
|
-
* If the input is
|
|
20
|
+
* If the input is maplike, it will be converted to a plain JavaScript object,
|
|
24
21
|
* recursively traversing the tree and converting all values to plain types.
|
|
25
22
|
*
|
|
26
23
|
* If the input is stringlike, its text will be returned.
|
|
@@ -33,9 +30,13 @@ import TypedArray from "./TypedArray.js";
|
|
|
33
30
|
* returned as a plain object.
|
|
34
31
|
*
|
|
35
32
|
* @param {any} input
|
|
33
|
+
* @param {import("../../index.ts").ReduceFn} [reduceFn]
|
|
36
34
|
* @returns {Promise<any>}
|
|
37
35
|
*/
|
|
38
|
-
export default async function toPlainValue(
|
|
36
|
+
export default async function toPlainValue(
|
|
37
|
+
input,
|
|
38
|
+
reduceFn = reduceToPlainObject
|
|
39
|
+
) {
|
|
39
40
|
if (input instanceof Function) {
|
|
40
41
|
// Invoke function
|
|
41
42
|
input = input();
|
|
@@ -47,17 +48,9 @@ export default async function toPlainValue(input) {
|
|
|
47
48
|
|
|
48
49
|
if (isPrimitive(input) || input instanceof Date) {
|
|
49
50
|
return input;
|
|
50
|
-
} else if (
|
|
51
|
+
} else if (isMaplike(input)) {
|
|
51
52
|
// 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
|
-
});
|
|
53
|
+
return mapReduce(input, (value) => toPlainValue(value, reduceFn), reduceFn);
|
|
61
54
|
} else if (input instanceof ArrayBuffer || input instanceof TypedArray) {
|
|
62
55
|
// Try to interpret the buffer as UTF-8 text, otherwise use base64.
|
|
63
56
|
const text = toString(input);
|
|
@@ -78,6 +71,15 @@ export default async function toPlainValue(input) {
|
|
|
78
71
|
}
|
|
79
72
|
}
|
|
80
73
|
|
|
74
|
+
function reduceToPlainObject(mapped, source) {
|
|
75
|
+
// Normalize slashes in keys.
|
|
76
|
+
// Special case for an empty map: if based on array, return array.
|
|
77
|
+
if (mapped.size === 0 && source instanceof ObjectMap) {
|
|
78
|
+
return /** @type {any} */ (source).object instanceof Array ? [] : {};
|
|
79
|
+
}
|
|
80
|
+
return castArraylike(mapped);
|
|
81
|
+
}
|
|
82
|
+
|
|
81
83
|
function toBase64(object) {
|
|
82
84
|
if (typeof Buffer !== "undefined") {
|
|
83
85
|
// 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,80 @@
|
|
|
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/MapTree.test.js"></script>
|
|
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/FunctionMap.test.js"></script>
|
|
22
|
+
<script type="module" src="../drivers/ObjectMap.test.js"></script>
|
|
23
|
+
<script type="module" src="../drivers/SetMap.test.js"></script>
|
|
24
|
+
<script type="module" src="../drivers/SyncMap.test.js"></script>
|
|
29
25
|
<script type="module" src="../operations/addNextPrevious.test.js"></script>
|
|
26
|
+
<script type="module" src="../operations/assign.test.js"></script>
|
|
30
27
|
<script type="module" src="../operations/cache.test.js"></script>
|
|
31
|
-
<script
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
<script
|
|
29
|
+
type="module"
|
|
30
|
+
src="../operations/cachedKeyFunctions.test.js"
|
|
31
|
+
></script>
|
|
32
|
+
<script type="module" src="../operations/clear.test.js"></script>
|
|
33
|
+
<script type="module" src="../operations/deepEntries.test.js"></script>
|
|
34
34
|
<script type="module" src="../operations/deepMerge.test.js"></script>
|
|
35
35
|
<script type="module" src="../operations/deepReverse.test.js"></script>
|
|
36
36
|
<script type="module" src="../operations/deepTake.test.js"></script>
|
|
37
37
|
<script type="module" src="../operations/deepText.test.js"></script>
|
|
38
38
|
<script type="module" src="../operations/deepValues.test.js"></script>
|
|
39
|
-
<script
|
|
40
|
-
|
|
39
|
+
<script
|
|
40
|
+
type="module"
|
|
41
|
+
src="../operations/deepValuesIterator.test.js"
|
|
42
|
+
></script>
|
|
43
|
+
<script type="module" src="../operations/delete.test.js"></script>
|
|
44
|
+
<script type="module" src="../operations/entries.test.js"></script>
|
|
45
|
+
<script
|
|
46
|
+
type="module"
|
|
47
|
+
src="../operations/extensionKeyFunctions.test.js"
|
|
48
|
+
></script>
|
|
41
49
|
<script type="module" src="../operations/filter.test.js"></script>
|
|
50
|
+
<script type="module" src="../operations/first.test.js"></script>
|
|
51
|
+
<script type="module" src="../operations/forEach.test.js"></script>
|
|
52
|
+
<script type="module" src="../operations/from.test.js"></script>
|
|
42
53
|
<script type="module" src="../operations/globKeys.test.js"></script>
|
|
43
|
-
<script type="module" src="../operations/
|
|
54
|
+
<script type="module" src="../operations/groupBy.test.js"></script>
|
|
55
|
+
<script type="module" src="../operations/groupBy.test.js"></script>
|
|
56
|
+
<script type="module" src="../operations/has.test.js"></script>
|
|
57
|
+
<script type="module" src="../operations/indent.test.js"></script>
|
|
58
|
+
<script type="module" src="../operations/inners.test.js"></script>
|
|
44
59
|
<script type="module" src="../operations/invokeFunctions.test.js"></script>
|
|
60
|
+
<script type="module" src="../operations/isMap.test.js"></script>
|
|
61
|
+
<script type="module" src="../operations/isMaplike.test.js"></script>
|
|
62
|
+
<script type="module" src="../operations/json.test.js"></script>
|
|
63
|
+
<script type="module" src="../operations/keys.test.js"></script>
|
|
64
|
+
<script type="module" src="../operations/length.test.js"></script>
|
|
45
65
|
<script type="module" src="../operations/map.test.js"></script>
|
|
66
|
+
<script type="module" src="../operations/mapExtension.test.js"></script>
|
|
67
|
+
<script type="module" src="../operations/mapReduce.test.js"></script>
|
|
46
68
|
<script type="module" src="../operations/mask.test.js"></script>
|
|
69
|
+
<script type="module" src="../operations/match.test.js"></script>
|
|
47
70
|
<script type="module" src="../operations/merge.test.js"></script>
|
|
48
71
|
<script type="module" src="../operations/paginate.test.js"></script>
|
|
49
72
|
<script type="module" src="../operations/parseExtensions.test.js"></script>
|
|
73
|
+
<script type="module" src="../operations/paths.test.js"></script>
|
|
74
|
+
<script type="module" src="../operations/plain.test.js"></script>
|
|
50
75
|
<script type="module" src="../operations/regExpKeys.test.js"></script>
|
|
51
76
|
<script type="module" src="../operations/reverse.test.js"></script>
|
|
52
77
|
<script type="module" src="../operations/scope.test.js"></script>
|
|
78
|
+
<script type="module" src="../operations/shuffle.test.js"></script>
|
|
53
79
|
<script type="module" src="../operations/sort.test.js"></script>
|
|
80
|
+
<script type="module" src="../operations/sync.test.js"></script>
|
|
54
81
|
<script type="module" src="../operations/take.test.js"></script>
|
|
55
|
-
<script type="module" src="../
|
|
82
|
+
<script type="module" src="../operations/text.test.js"></script>
|
|
83
|
+
<script type="module" src="../operations/toFunction.test.js"></script>
|
|
84
|
+
<script type="module" src="../operations/traverse.test.js"></script>
|
|
85
|
+
<script type="module" src="../operations/traversePath.test.js"></script>
|
|
86
|
+
<script type="module" src="../operations/values.test.js"></script>
|
|
87
|
+
<script type="module" src="../operations/withKeys.test.js"></script>
|
|
56
88
|
</head>
|
|
57
89
|
<body></body>
|
|
58
90
|
</html>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import AsyncMap from "../../src/drivers/AsyncMap.js";
|
|
4
|
+
import SampleAsyncMap from "../SampleAsyncMap.js";
|
|
5
|
+
|
|
6
|
+
describe("AsyncMap", () => {
|
|
7
|
+
test("clear", async () => {
|
|
8
|
+
const map = new SampleAsyncMap([
|
|
9
|
+
["a", 1],
|
|
10
|
+
["b", 2],
|
|
11
|
+
]);
|
|
12
|
+
assert.strictEqual(await map.size, 2);
|
|
13
|
+
await map.clear();
|
|
14
|
+
assert.strictEqual(await map.size, 0);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("entries invokes keys() and get()", async () => {
|
|
18
|
+
const map = new SampleAsyncMap([
|
|
19
|
+
["a", 1],
|
|
20
|
+
["b", 2],
|
|
21
|
+
]);
|
|
22
|
+
const entries = [];
|
|
23
|
+
for await (const entry of map.entries()) {
|
|
24
|
+
entries.push(entry);
|
|
25
|
+
}
|
|
26
|
+
assert.deepStrictEqual(entries, [
|
|
27
|
+
["a", 1],
|
|
28
|
+
["b", 2],
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("forEach", async () => {
|
|
33
|
+
const map = new SampleAsyncMap([
|
|
34
|
+
["a", 1],
|
|
35
|
+
["b", 2],
|
|
36
|
+
]);
|
|
37
|
+
const calls = [];
|
|
38
|
+
await map.forEach(async (value, key, theMap) => {
|
|
39
|
+
calls.push([key, value, theMap]);
|
|
40
|
+
});
|
|
41
|
+
assert.deepStrictEqual(calls, [
|
|
42
|
+
["a", 1, map],
|
|
43
|
+
["b", 2, map],
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("static groupBy", async () => {
|
|
48
|
+
const items = [
|
|
49
|
+
{ name: "apple", type: "fruit" },
|
|
50
|
+
{ name: "beet", type: "vegetable" },
|
|
51
|
+
{ name: "cherry", type: "fruit" },
|
|
52
|
+
];
|
|
53
|
+
const map = await AsyncMap.groupBy(
|
|
54
|
+
items,
|
|
55
|
+
async (element, index) => element.type
|
|
56
|
+
);
|
|
57
|
+
assert.deepStrictEqual(Array.from(map.entries()), [
|
|
58
|
+
[
|
|
59
|
+
"fruit",
|
|
60
|
+
[
|
|
61
|
+
{ name: "apple", type: "fruit" },
|
|
62
|
+
{ name: "cherry", type: "fruit" },
|
|
63
|
+
],
|
|
64
|
+
],
|
|
65
|
+
["vegetable", [{ name: "beet", type: "vegetable" }]],
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("has returns true if key exists in keys()", async () => {
|
|
70
|
+
const map = new AsyncMap();
|
|
71
|
+
map.keys = async function* () {
|
|
72
|
+
yield* ["a", "b/"];
|
|
73
|
+
};
|
|
74
|
+
assert.strictEqual(await map.has("a"), true);
|
|
75
|
+
assert.strictEqual(await map.has("b"), true); // trailing slash optional
|
|
76
|
+
assert.strictEqual(await map.has("b/"), true);
|
|
77
|
+
assert.strictEqual(await map.has("c"), false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("readOnly if get() is overridden but not delete() and set()", () => {
|
|
81
|
+
const map1 = new AsyncMap();
|
|
82
|
+
assert.strictEqual(map1.readOnly, false);
|
|
83
|
+
|
|
84
|
+
const map2 = new AsyncMap();
|
|
85
|
+
map2.get = async (key) => null;
|
|
86
|
+
assert.strictEqual(map2.readOnly, true);
|
|
87
|
+
|
|
88
|
+
const map3 = new AsyncMap();
|
|
89
|
+
map3.delete = async (key) => false;
|
|
90
|
+
map3.get = async (key) => null;
|
|
91
|
+
map3.set = async (key, value) => map3;
|
|
92
|
+
assert.strictEqual(map3.readOnly, false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("size", async () => {
|
|
96
|
+
const map = new SampleAsyncMap();
|
|
97
|
+
assert.strictEqual(await map.size, 0);
|
|
98
|
+
await map.set("a", 1);
|
|
99
|
+
assert.strictEqual(await map.size, 1);
|
|
100
|
+
await map.set("b", 2);
|
|
101
|
+
assert.strictEqual(await map.size, 2);
|
|
102
|
+
await map.delete("a");
|
|
103
|
+
assert.strictEqual(await map.size, 1);
|
|
104
|
+
await map.clear();
|
|
105
|
+
assert.strictEqual(await map.size, 0);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("values", async () => {
|
|
109
|
+
const map = new SampleAsyncMap([
|
|
110
|
+
["a", 1],
|
|
111
|
+
["b", 2],
|
|
112
|
+
]);
|
|
113
|
+
const values = [];
|
|
114
|
+
for await (const value of map.values()) {
|
|
115
|
+
values.push(value);
|
|
116
|
+
}
|
|
117
|
+
assert.deepStrictEqual(values, [1, 2]);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import BrowserFileMap from "../../src/drivers/BrowserFileMap.js";
|
|
4
|
+
import keys from "../../src/operations/keys.js";
|
|
5
|
+
import map from "../../src/operations/map.js";
|
|
6
|
+
import plain from "../../src/operations/plain.js";
|
|
5
7
|
|
|
6
8
|
// Skip these tests if we're not in a browser.
|
|
7
9
|
const isBrowser = typeof window !== "undefined";
|
|
8
10
|
if (isBrowser) {
|
|
9
|
-
describe("
|
|
11
|
+
describe("BrowserFileMap", async () => {
|
|
10
12
|
test("can get the keys of the tree", async () => {
|
|
11
13
|
const fixture = await createFixture();
|
|
12
|
-
assert.deepEqual(
|
|
14
|
+
assert.deepEqual(await keys(fixture), [
|
|
13
15
|
"Alice.md",
|
|
14
16
|
"Bob.md",
|
|
15
17
|
"Carol.md",
|
|
@@ -57,6 +59,24 @@ if (isBrowser) {
|
|
|
57
59
|
assert(await fixture.get("subfolder/"));
|
|
58
60
|
});
|
|
59
61
|
|
|
62
|
+
test("can delete a value", async () => {
|
|
63
|
+
const fixture = await createFixture();
|
|
64
|
+
|
|
65
|
+
// Delete existing key.
|
|
66
|
+
const deleted = await fixture.delete("Bob.md");
|
|
67
|
+
assert.equal(deleted, true);
|
|
68
|
+
|
|
69
|
+
// Delete non-existing key.
|
|
70
|
+
const deletedAgain = await fixture.delete("Bob.md");
|
|
71
|
+
assert.equal(deletedAgain, false);
|
|
72
|
+
|
|
73
|
+
assert.deepEqual(await strings(fixture), {
|
|
74
|
+
"Alice.md": "Hello, **Alice**.",
|
|
75
|
+
"Carol.md": "Hello, **Carol**.",
|
|
76
|
+
subfolder: {},
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
60
80
|
test("can set a value", async () => {
|
|
61
81
|
const fixture = await createFixture();
|
|
62
82
|
|
|
@@ -66,41 +86,38 @@ if (isBrowser) {
|
|
|
66
86
|
// New key.
|
|
67
87
|
await fixture.set("David.md", "Hello, **David**.");
|
|
68
88
|
|
|
69
|
-
// Delete key.
|
|
70
|
-
await fixture.set("Bob.md", undefined);
|
|
71
|
-
|
|
72
|
-
// Delete non-existent key.
|
|
73
|
-
await fixture.set("xyz", undefined);
|
|
74
|
-
|
|
75
89
|
assert.deepEqual(await strings(fixture), {
|
|
76
90
|
"Alice.md": "Goodbye, **Alice**.",
|
|
91
|
+
"Bob.md": "Hello, **Bob**.",
|
|
77
92
|
"Carol.md": "Hello, **Carol**.",
|
|
78
93
|
"David.md": "Hello, **David**.",
|
|
79
94
|
subfolder: {},
|
|
80
95
|
});
|
|
81
96
|
});
|
|
82
97
|
|
|
83
|
-
test("can create a subfolder via
|
|
98
|
+
test("can return or create a subfolder via child", async () => {
|
|
84
99
|
const fixture = await createFixture();
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
|
|
101
|
+
// Return existing subfolder
|
|
102
|
+
const subfolder = await fixture.get("subfolder");
|
|
103
|
+
const child = await fixture.child("subfolder");
|
|
104
|
+
assert.equal(child, subfolder);
|
|
105
|
+
|
|
106
|
+
// Create new subfolder
|
|
107
|
+
const newChild = await fixture.child("newSubfolder");
|
|
108
|
+
assert.equal(newChild, subfolder);
|
|
109
|
+
|
|
110
|
+
// Replace existing file with folder
|
|
111
|
+
const replacedChild = await fixture.child("Alice.md");
|
|
112
|
+
assert.equal(replacedChild.parent, fixture);
|
|
113
|
+
|
|
114
|
+
assert.deepEqual(await keys(fixture), [
|
|
115
|
+
"Alice.md/",
|
|
116
|
+
"Bob.md",
|
|
117
|
+
"Carol.md",
|
|
118
|
+
"subfolder/",
|
|
119
|
+
"newSubfolder/",
|
|
120
|
+
]);
|
|
104
121
|
});
|
|
105
122
|
});
|
|
106
123
|
}
|
|
@@ -141,12 +158,12 @@ async function createFixture() {
|
|
|
141
158
|
create: true,
|
|
142
159
|
});
|
|
143
160
|
|
|
144
|
-
return new
|
|
161
|
+
return new BrowserFileMap(subdirectory);
|
|
145
162
|
}
|
|
146
163
|
|
|
147
164
|
async function strings(tree) {
|
|
148
|
-
return
|
|
149
|
-
|
|
165
|
+
return plain(
|
|
166
|
+
await map(tree, {
|
|
150
167
|
deep: true,
|
|
151
168
|
value: (value) => text(value),
|
|
152
169
|
})
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { describe, test } from "node:test";
|
|
3
|
-
import
|
|
3
|
+
import CalendarMap from "../../src/drivers/CalendarMap.js";
|
|
4
4
|
import toPlainValue from "../../src/utilities/toPlainValue.js";
|
|
5
5
|
|
|
6
|
-
describe("
|
|
7
|
-
test("without a start or end, returns a
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
});
|
|
11
|
-
const plain = await toPlainValue(tree);
|
|
6
|
+
describe("CalendarMap", () => {
|
|
7
|
+
test("without a start or end, returns a map for today", async () => {
|
|
8
|
+
const map = new CalendarMap();
|
|
9
|
+
const plain = await toPlainValue(map);
|
|
12
10
|
const today = new Date();
|
|
13
11
|
const year = today.getFullYear();
|
|
14
12
|
const month = (today.getMonth() + 1).toString().padStart(2, "0");
|
|
@@ -22,13 +20,12 @@ describe("calendarTree", () => {
|
|
|
22
20
|
});
|
|
23
21
|
});
|
|
24
22
|
|
|
25
|
-
test("returns a
|
|
26
|
-
const
|
|
23
|
+
test("returns a map for a month range", async () => {
|
|
24
|
+
const map = new CalendarMap({
|
|
27
25
|
start: "2025-01",
|
|
28
26
|
end: "2025-02",
|
|
29
|
-
value: (year, month, day) => `${year}-${month}-${day}`,
|
|
30
27
|
});
|
|
31
|
-
const plain = await toPlainValue(
|
|
28
|
+
const plain = await toPlainValue(map);
|
|
32
29
|
assert.deepEqual(plain, {
|
|
33
30
|
2025: {
|
|
34
31
|
"01": {
|
|
@@ -98,22 +95,23 @@ describe("calendarTree", () => {
|
|
|
98
95
|
});
|
|
99
96
|
});
|
|
100
97
|
|
|
101
|
-
test("returns a
|
|
102
|
-
const
|
|
98
|
+
test("returns a map for a day range", async () => {
|
|
99
|
+
const map = new CalendarMap({
|
|
103
100
|
start: "2025-02-27",
|
|
104
101
|
end: "2025-03-02",
|
|
105
|
-
|
|
102
|
+
// Exercise custom value function
|
|
103
|
+
value: (year, month, day) => `${year}.${month}.${day}`,
|
|
106
104
|
});
|
|
107
|
-
const plain = await toPlainValue(
|
|
105
|
+
const plain = await toPlainValue(map);
|
|
108
106
|
assert.deepEqual(plain, {
|
|
109
107
|
2025: {
|
|
110
108
|
"02": {
|
|
111
|
-
27: "2025
|
|
112
|
-
28: "2025
|
|
109
|
+
27: "2025.02.27",
|
|
110
|
+
28: "2025.02.28",
|
|
113
111
|
},
|
|
114
112
|
"03": {
|
|
115
|
-
"01": "2025
|
|
116
|
-
"02": "2025
|
|
113
|
+
"01": "2025.03.01",
|
|
114
|
+
"02": "2025.03.02",
|
|
117
115
|
},
|
|
118
116
|
},
|
|
119
117
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import ConstantMap from "../../src/drivers/ConstantMap.js";
|
|
4
|
+
import keys from "../../src/operations/keys.js";
|
|
5
|
+
import traverse from "../../src/operations/traverse.js";
|
|
6
|
+
|
|
7
|
+
describe("ConstantMap", () => {
|
|
8
|
+
test("returns a deep tree that returns constant for all keys", async () => {
|
|
9
|
+
const fixture = new ConstantMap(1);
|
|
10
|
+
assert.deepEqual(Array.from(await keys(fixture)), []);
|
|
11
|
+
assert.equal(fixture.get("a"), 1);
|
|
12
|
+
assert.equal(fixture.get("b"), 1);
|
|
13
|
+
assert.equal(await traverse(fixture, "c/", "d/", "e"), 1);
|
|
14
|
+
});
|
|
15
|
+
});
|