@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.
Files changed (178) hide show
  1. package/browser.js +1 -1
  2. package/index.ts +31 -35
  3. package/main.js +1 -2
  4. package/package.json +4 -7
  5. package/shared.js +77 -12
  6. package/src/Tree.js +8 -2
  7. package/src/drivers/AsyncMap.js +210 -0
  8. package/src/drivers/{BrowserFileTree.js → BrowserFileMap.js} +36 -27
  9. package/src/drivers/{calendarTree.js → CalendarMap.js} +81 -62
  10. package/src/drivers/ConstantMap.js +30 -0
  11. package/src/drivers/DeepObjectMap.js +27 -0
  12. package/src/drivers/{ExplorableSiteTree.js → ExplorableSiteMap.js} +7 -7
  13. package/src/drivers/FileMap.js +245 -0
  14. package/src/drivers/{FunctionTree.js → FunctionMap.js} +19 -22
  15. package/src/drivers/ObjectMap.js +139 -0
  16. package/src/drivers/SetMap.js +13 -0
  17. package/src/drivers/{SiteTree.js → SiteMap.js} +16 -17
  18. package/src/drivers/SyncMap.js +245 -0
  19. package/src/jsonKeys.d.ts +2 -2
  20. package/src/jsonKeys.js +6 -5
  21. package/src/operations/addNextPrevious.js +35 -36
  22. package/src/operations/assign.js +30 -21
  23. package/src/operations/cache.js +29 -35
  24. package/src/operations/cachedKeyFunctions.js +1 -1
  25. package/src/operations/calendar.js +5 -0
  26. package/src/operations/clear.js +13 -12
  27. package/src/operations/constant.js +5 -0
  28. package/src/operations/deepEntries.js +23 -0
  29. package/src/operations/deepMap.js +9 -9
  30. package/src/operations/deepMerge.js +36 -25
  31. package/src/operations/deepReverse.js +23 -16
  32. package/src/operations/deepTake.js +7 -7
  33. package/src/operations/deepText.js +4 -4
  34. package/src/operations/deepValues.js +3 -6
  35. package/src/operations/deepValuesIterator.js +11 -11
  36. package/src/operations/delete.js +8 -12
  37. package/src/operations/entries.js +17 -10
  38. package/src/operations/filter.js +9 -7
  39. package/src/operations/first.js +12 -10
  40. package/src/operations/forEach.js +10 -13
  41. package/src/operations/from.js +31 -39
  42. package/src/operations/globKeys.js +22 -17
  43. package/src/operations/group.js +2 -2
  44. package/src/operations/groupBy.js +24 -22
  45. package/src/operations/has.js +7 -9
  46. package/src/operations/indent.js +2 -2
  47. package/src/operations/inners.js +19 -15
  48. package/src/operations/invokeFunctions.js +22 -10
  49. package/src/operations/isAsyncMutableTree.js +5 -12
  50. package/src/operations/isAsyncTree.js +5 -20
  51. package/src/operations/isMap.js +39 -0
  52. package/src/operations/isMaplike.js +34 -0
  53. package/src/operations/isReadOnlyMap.js +14 -0
  54. package/src/operations/isTraversable.js +3 -3
  55. package/src/operations/isTreelike.js +5 -30
  56. package/src/operations/json.js +4 -12
  57. package/src/operations/keys.js +17 -8
  58. package/src/operations/length.js +9 -8
  59. package/src/operations/map.js +27 -30
  60. package/src/operations/mapExtension.js +20 -16
  61. package/src/operations/mapReduce.js +22 -17
  62. package/src/operations/mask.js +31 -22
  63. package/src/operations/match.js +13 -9
  64. package/src/operations/merge.js +43 -35
  65. package/src/operations/paginate.js +26 -18
  66. package/src/operations/parent.js +7 -7
  67. package/src/operations/paths.js +8 -8
  68. package/src/operations/plain.js +6 -6
  69. package/src/operations/regExpKeys.js +21 -12
  70. package/src/operations/reverse.js +21 -15
  71. package/src/operations/root.js +6 -5
  72. package/src/operations/scope.js +31 -26
  73. package/src/operations/shuffle.js +23 -16
  74. package/src/operations/size.js +13 -0
  75. package/src/operations/sort.js +55 -40
  76. package/src/operations/sync.js +21 -0
  77. package/src/operations/take.js +23 -11
  78. package/src/operations/text.js +4 -4
  79. package/src/operations/toFunction.js +7 -7
  80. package/src/operations/traverse.js +4 -4
  81. package/src/operations/traverseOrThrow.js +13 -9
  82. package/src/operations/traversePath.js +2 -2
  83. package/src/operations/values.js +18 -9
  84. package/src/operations/withKeys.js +22 -16
  85. package/src/symbols.js +1 -0
  86. package/src/utilities/castArraylike.js +10 -2
  87. package/src/utilities/getMapArgument.js +38 -0
  88. package/src/utilities/getParent.js +2 -2
  89. package/src/utilities/isStringlike.js +7 -5
  90. package/src/utilities/setParent.js +7 -7
  91. package/src/utilities/toFunction.js +2 -2
  92. package/src/utilities/toPlainValue.js +22 -18
  93. package/test/SampleAsyncMap.js +34 -0
  94. package/test/browser/assert.js +20 -0
  95. package/test/browser/index.html +54 -21
  96. package/test/drivers/AsyncMap.test.js +119 -0
  97. package/test/drivers/{BrowserFileTree.test.js → BrowserFileMap.test.js} +42 -23
  98. package/test/drivers/{calendarTree.test.js → CalendarMap.test.js} +17 -19
  99. package/test/drivers/ConstantMap.test.js +15 -0
  100. package/test/drivers/DeepObjectMap.test.js +36 -0
  101. package/test/drivers/{ExplorableSiteTree.test.js → ExplorableSiteMap.test.js} +29 -14
  102. package/test/drivers/FileMap.test.js +185 -0
  103. package/test/drivers/FunctionMap.test.js +56 -0
  104. package/test/drivers/ObjectMap.test.js +166 -0
  105. package/test/drivers/SetMap.test.js +35 -0
  106. package/test/drivers/{SiteTree.test.js → SiteMap.test.js} +14 -10
  107. package/test/drivers/SyncMap.test.js +321 -0
  108. package/test/jsonKeys.test.js +2 -2
  109. package/test/operations/addNextPrevious.test.js +3 -2
  110. package/test/operations/assign.test.js +30 -35
  111. package/test/operations/cache.test.js +8 -6
  112. package/test/operations/cachedKeyFunctions.test.js +6 -5
  113. package/test/operations/clear.test.js +6 -27
  114. package/test/operations/deepEntries.test.js +32 -0
  115. package/test/operations/deepMerge.test.js +6 -5
  116. package/test/operations/deepReverse.test.js +2 -2
  117. package/test/operations/deepTake.test.js +2 -2
  118. package/test/operations/deepText.test.js +4 -4
  119. package/test/operations/deepValuesIterator.test.js +2 -2
  120. package/test/operations/delete.test.js +2 -2
  121. package/test/operations/extensionKeyFunctions.test.js +6 -5
  122. package/test/operations/filter.test.js +3 -3
  123. package/test/operations/from.test.js +23 -31
  124. package/test/operations/globKeys.test.js +9 -9
  125. package/test/operations/groupBy.test.js +6 -5
  126. package/test/operations/inners.test.js +4 -4
  127. package/test/operations/invokeFunctions.test.js +2 -2
  128. package/test/operations/isMap.test.js +15 -0
  129. package/test/operations/isMaplike.test.js +15 -0
  130. package/test/operations/json.test.js +2 -2
  131. package/test/operations/keys.test.js +16 -3
  132. package/test/operations/map.test.js +20 -18
  133. package/test/operations/mapExtension.test.js +6 -6
  134. package/test/operations/mapReduce.test.js +2 -2
  135. package/test/operations/mask.test.js +4 -3
  136. package/test/operations/match.test.js +2 -2
  137. package/test/operations/merge.test.js +15 -11
  138. package/test/operations/paginate.test.js +5 -5
  139. package/test/operations/parent.test.js +3 -3
  140. package/test/operations/paths.test.js +6 -6
  141. package/test/operations/plain.test.js +8 -8
  142. package/test/operations/regExpKeys.test.js +12 -11
  143. package/test/operations/reverse.test.js +4 -3
  144. package/test/operations/scope.test.js +6 -5
  145. package/test/operations/shuffle.test.js +3 -2
  146. package/test/operations/sort.test.js +7 -10
  147. package/test/operations/sync.test.js +43 -0
  148. package/test/operations/take.test.js +2 -2
  149. package/test/operations/toFunction.test.js +2 -2
  150. package/test/operations/traverse.test.js +4 -5
  151. package/test/operations/withKeys.test.js +2 -2
  152. package/test/utilities/setParent.test.js +6 -6
  153. package/test/utilities/toFunction.test.js +2 -2
  154. package/test/utilities/toPlainValue.test.js +51 -12
  155. package/src/drivers/DeepMapTree.js +0 -23
  156. package/src/drivers/DeepObjectTree.js +0 -18
  157. package/src/drivers/DeferredTree.js +0 -81
  158. package/src/drivers/FileTree.js +0 -276
  159. package/src/drivers/MapTree.js +0 -70
  160. package/src/drivers/ObjectTree.js +0 -158
  161. package/src/drivers/SetTree.js +0 -34
  162. package/src/drivers/constantTree.js +0 -19
  163. package/src/drivers/limitConcurrency.js +0 -63
  164. package/src/internal.js +0 -16
  165. package/src/utilities/getTreeArgument.js +0 -43
  166. package/test/drivers/DeepMapTree.test.js +0 -17
  167. package/test/drivers/DeepObjectTree.test.js +0 -35
  168. package/test/drivers/DeferredTree.test.js +0 -22
  169. package/test/drivers/FileTree.test.js +0 -192
  170. package/test/drivers/FunctionTree.test.js +0 -46
  171. package/test/drivers/MapTree.test.js +0 -59
  172. package/test/drivers/ObjectTree.test.js +0 -163
  173. package/test/drivers/SetTree.test.js +0 -44
  174. package/test/drivers/constantTree.test.js +0 -13
  175. package/test/drivers/limitConcurrency.test.js +0 -41
  176. package/test/operations/isAsyncMutableTree.test.js +0 -17
  177. package/test/operations/isAsyncTree.test.js +0 -26
  178. package/test/operations/isTreelike.test.js +0 -13
@@ -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 getTreeArgument from "../utilities/getTreeArgument.js";
4
+ import getMapArgument from "../utilities/getMapArgument.js";
5
+ import keys from "./keys.js";
3
6
 
4
7
  /**
5
- * Return a new grouping of the treelike's values into chunks of the specified
8
+ * Return a new grouping of the map's values into chunks of the specified
6
9
  * size.
7
10
  *
8
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
9
- * @typedef {import("../../index.ts").Treelike} Treelike
11
+ * @typedef {import("../../index.ts").Maplike} Maplike
10
12
  *
11
- * @param {Treelike} treelike
13
+ * @param {Maplike} maplike
12
14
  * @param {number} [size=10]
13
15
  */
14
- export default async function paginate(treelike, size = 10) {
15
- const tree = await getTreeArgument(treelike, "paginate");
16
+ export default async function paginate(maplike, size = 10) {
17
+ const source = await getMapArgument(maplike, "paginate");
16
18
 
17
- const keys = Array.from(await tree.keys());
18
- const pageCount = Math.ceil(keys.length / size);
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 Map();
34
+ const items = new SyncMap();
31
35
  for (
32
36
  let index = (pageNumber - 1) * size;
33
- index < Math.min(keys.length, pageNumber * size);
37
+ index < Math.min(treeKeys.length, pageNumber * size);
34
38
  index++
35
39
  ) {
36
- const key = keys[index];
37
- items.set(key, await tree.get(keys[index]));
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 an array from 1..totalPages
51
- return Array.from({ length: pageCount }, (_, index) => index + 1);
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
  }
@@ -1,13 +1,13 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
2
 
3
3
  /**
4
- * Returns the parent of the current tree.
4
+ * Returns the parent of the map, if any.
5
5
  *
6
- * @typedef {import("../../index.ts").Treelike} Treelike
6
+ * @typedef {import("../../index.ts").Maplike} Maplike
7
7
  *
8
- * @param {Treelike} treelike
8
+ * @param {Maplike} maplike
9
9
  */
10
- export default async function parent(treelike) {
11
- const tree = await getTreeArgument(treelike, "parent");
12
- return tree.parent;
10
+ export default async function parent(maplike) {
11
+ const map = await getMapArgument(maplike, "parent");
12
+ return "parent" in map ? map.parent : undefined;
13
13
  }
@@ -1,6 +1,6 @@
1
1
  import * as trailingSlash from "../trailingSlash.js";
2
- import from from "./from.js";
3
- import isAsyncTree from "./isAsyncTree.js";
2
+ import getMapArgument from "../utilities/getMapArgument.js";
3
+ import isMap from "./isMap.js";
4
4
 
5
5
  /**
6
6
  * Returns slash-separated paths for all values in the tree.
@@ -10,17 +10,17 @@ import isAsyncTree from "./isAsyncTree.js";
10
10
  * If `assumeSlashes` is true, then keys are assumed to have trailing slashes to
11
11
  * indicate subtrees. The default value of this option is false.
12
12
  *
13
- * @typedef {import("../../index.ts").Treelike} Treelike
13
+ * @typedef {import("../../index.ts").Maplike} Maplike
14
14
  *
15
- * @param {Treelike} treelike
15
+ * @param {Maplike} maplike
16
16
  * @param {{ assumeSlashes?: boolean, base?: string }} options
17
17
  */
18
- export default async function paths(treelike, options = {}) {
19
- const tree = from(treelike);
18
+ export default async function paths(maplike, options = {}) {
19
+ const tree = await getMapArgument(maplike, "paths");
20
20
  const base = options.base ?? "";
21
21
  const assumeSlashes = options.assumeSlashes ?? false;
22
22
  const result = [];
23
- for (const key of await tree.keys()) {
23
+ for await (const key of tree.keys()) {
24
24
  const separator = trailingSlash.has(base) ? "" : "/";
25
25
  const valuePath = base ? `${base}${separator}${key}` : key;
26
26
  let isSubtree;
@@ -38,7 +38,7 @@ export default async function paths(treelike, options = {}) {
38
38
  }
39
39
  if (value) {
40
40
  // If we got the value we can check if it's a subtree
41
- isSubtree = isAsyncTree(value);
41
+ isSubtree = isMap(value);
42
42
  }
43
43
  if (isSubtree) {
44
44
  const subPaths = await paths(value, { assumeSlashes, base: valuePath });
@@ -1,4 +1,4 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
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").Treelike} Treelike
23
+ * @typedef {import("../../index.ts").Maplike} Maplike
24
24
  * @typedef {import("../../index.ts").PlainObject} PlainObject
25
25
  *
26
- * @param {Treelike} treelike
26
+ * @param {Maplike} maplike
27
27
  */
28
- export default async function plain(treelike) {
29
- if (treelike instanceof Function) {
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 getTreeArgument(treelike, "plain");
32
+ const tree = await getMapArgument(maplike, "plain");
33
33
  return toPlainValue(tree);
34
34
  }
@@ -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 getTreeArgument from "../utilities/getTreeArgument.js";
3
- import isAsyncTree from "./isAsyncTree.js";
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
- * @type {import("../../index.ts").TreeTransform}
15
+ * @typedef {import("../../index.ts").Maplike} Maplike
16
+ *
17
+ * @param {Maplike} maplike
18
+ * @returns {Promise<AsyncMap>}
14
19
  */
15
- export default async function regExpKeys(treelike) {
16
- const tree = await getTreeArgument(treelike, "regExpKeys");
20
+ export default async function regExpKeys(maplike) {
21
+ const source = await getMapArgument(maplike, "regExpKeys", { deep: true });
17
22
 
18
- const map = new Map();
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 await tree.keys()) {
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 tree.get(key);
65
+ let value = await source.get(key);
57
66
 
58
67
  let regExp;
59
- if (trailingSlash.has(key) || isAsyncTree(value)) {
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 getTreeArgument from "../utilities/getTreeArgument.js";
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
- * Reverse the order of the top-level keys in the tree.
6
+ * Return a new map with the keys reversed.
5
7
  *
6
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
7
- * @typedef {import("../../index.ts").Treelike} Treelike
8
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
9
  *
9
- * @param {Treelike} treelike
10
- * @returns {Promise<AsyncTree>}
10
+ * @param {Maplike} maplike
11
+ * @returns {Promise<AsyncMap>}
11
12
  */
12
- export default async function reverse(treelike) {
13
- const tree = await getTreeArgument(treelike, "reverse");
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 tree.get(key);
19
+ return source.get(key);
18
20
  },
19
21
 
20
- async keys() {
21
- const keys = Array.from(await tree.keys());
22
- keys.reverse();
23
- return keys;
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
  }
@@ -1,15 +1,16 @@
1
1
  import * as symbols from "../symbols.js";
2
- import from from "./from.js";
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").Treelike} Treelike
7
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
8
  *
9
- * @param {Treelike} treelike
9
+ * @param {Maplike} maplike
10
10
  */
11
- export default function root(treelike) {
12
- let current = from(treelike);
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
  }
@@ -1,48 +1,49 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import AsyncMap from "../drivers/AsyncMap.js";
2
+ import getMapArgument from "../utilities/getMapArgument.js";
2
3
 
3
4
  /**
4
- * A tree's "scope" is the collection of everything in that tree and all of its
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("@weborigami/types").AsyncTree} AsyncTree
8
- * @typedef {import("../../index.ts").Treelike} Treelike
8
+ * @typedef {import("../../index.ts").Maplike} Maplike
9
9
  *
10
- * @param {Treelike} treelike
11
- * @returns {Promise<AsyncTree & {trees: AsyncTree[]}>}
10
+ * @param {Maplike} maplike
11
+ * @returns {Promise<AsyncMap>}
12
12
  */
13
- export default async function scope(treelike) {
14
- const tree = await getTreeArgument(treelike, "scope");
13
+ export default async function scope(maplike) {
14
+ const source = await getMapArgument(maplike, "scope");
15
15
 
16
- return {
17
- // Starting with this tree, search up the parent hierarchy.
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 {AsyncTree|null|undefined} */
20
- let current = tree;
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 keys = new Set();
35
-
36
- /** @type {AsyncTree|null|undefined} */
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 await current.keys()) {
40
- keys.add(key);
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
- return keys;
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 {AsyncTree|null|undefined} */
56
- let current = tree;
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
  }
@@ -1,33 +1,40 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
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 tree with the original's keys shuffled
5
7
  *
6
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
7
- * @typedef {import("../../index.ts").Treelike} Treelike
8
+ * @typedef {import("../../index.ts").Maplike} Maplike
8
9
  *
9
- * @param {Treelike} treelike
10
+ * @param {Maplike} maplike
10
11
  * @param {boolean?} reshuffle
11
- * @returns {Promise<AsyncTree>}
12
+ * @returns {Promise<AsyncMap>}
12
13
  */
13
- export default async function shuffle(treelike, reshuffle = false) {
14
- const tree = await getTreeArgument(treelike, "shuffle");
14
+ export default async function shuffle(maplike, reshuffle = false) {
15
+ const source = await getMapArgument(maplike, "shuffle");
15
16
 
16
- let keys;
17
+ let mapKeys;
18
+
19
+ return Object.assign(new AsyncMap(), {
20
+ description: "shuffle",
17
21
 
18
- return {
19
22
  async get(key) {
20
- return tree.get(key);
23
+ return source.get(key);
21
24
  },
22
25
 
23
- async keys() {
24
- if (!keys || reshuffle) {
25
- keys = Array.from(await tree.keys());
26
- shuffleArray(keys);
26
+ async *keys() {
27
+ if (!mapKeys || reshuffle) {
28
+ mapKeys = await keys(source);
29
+ shuffleArray(mapKeys);
27
30
  }
28
- return keys;
31
+ yield* mapKeys;
29
32
  },
30
- };
33
+
34
+ source,
35
+
36
+ trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
37
+ });
31
38
  }
32
39
 
33
40
  /*
@@ -0,0 +1,13 @@
1
+ import getMapArgument from "../utilities/getMapArgument.js";
2
+
3
+ /**
4
+ * Return the number of keys in the map.
5
+ *
6
+ * @typedef {import("../../index.ts").Maplike} Maplike
7
+ *
8
+ * @param {Maplike} maplike
9
+ */
10
+ export default async function size(maplike) {
11
+ const map = await getMapArgument(maplike, "size");
12
+ return map.size;
13
+ }
@@ -1,21 +1,23 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
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
- * Return a new tree with the original's keys sorted. A comparison function can
6
+ * Return a new map with the original's keys sorted. A comparison function can
5
7
  * be provided; by default the keys will be sorted in [natural sort
6
8
  * order](https://en.wikipedia.org/wiki/Natural_sort_order).
7
9
  *
8
- * @typedef {import("@weborigami/types").AsyncTree} AsyncTree
9
- * @typedef {(key: any, tree: AsyncTree) => any} SortKeyFn
10
+ * @typedef {import("../../index.ts").SyncOrAsyncMap} SyncOrAsyncMap
11
+ * @typedef {(key: any, map: SyncOrAsyncMap) => any} SortKeyFn
10
12
  * @typedef {{ compare?: (a: any, b: any) => number, sortKey?: SortKeyFn }} SortOptions
11
- * @typedef {import("../../index.ts").Treelike} Treelike
13
+ * @typedef {import("../../index.ts").Maplike} Maplike
12
14
  * @typedef {import("../../index.ts").ValueKeyFn} ValueKeyFn
13
15
  *
14
- * @param {Treelike} treelike
16
+ * @param {Maplike} maplike
15
17
  * @param {SortOptions|ValueKeyFn} [options]
16
18
  */
17
- export default async function sort(treelike, options) {
18
- const tree = await getTreeArgument(treelike, "sort");
19
+ export default async function sort(maplike, options) {
20
+ const source = await getMapArgument(maplike, "sort");
19
21
 
20
22
  let sortKey;
21
23
  let compare;
@@ -27,39 +29,52 @@ export default async function sort(treelike, options) {
27
29
  sortKey = options?.sortKey;
28
30
  }
29
31
 
30
- const transformed = Object.create(tree);
31
- transformed.keys = async () => {
32
- const keys = Array.from(await tree.keys());
32
+ const transformed = Object.assign(new AsyncMap(), {
33
+ descriptor: "sort",
33
34
 
34
- if (sortKey) {
35
- // Invoke the async sortKey function to get sort keys.
36
- // Create { key, sortKey } tuples.
37
- const tuples = await Promise.all(
38
- keys.map(async (key) => {
39
- const value = await tree.get(key);
40
- const sort = await sortKey(value, key, tree);
41
- if (sort === undefined) {
42
- throw new Error(
43
- `sortKey function returned undefined for key ${key}`
44
- );
45
- }
46
- return { key, sort };
47
- })
48
- );
35
+ async get(key) {
36
+ return source.get(key);
37
+ },
38
+
39
+ async *keys() {
40
+ const treeKeys = await keys(source);
41
+
42
+ let resultKeys;
43
+ if (sortKey) {
44
+ // Invoke the async sortKey function to get sort keys.
45
+ // Create { key, sortKey } tuples.
46
+ const tuples = await Promise.all(
47
+ treeKeys.map(async (key) => {
48
+ const value = await source.get(key);
49
+ const sort = await sortKey(value, key, source);
50
+ if (sort === undefined) {
51
+ throw new Error(
52
+ `sortKey function returned undefined for key ${key}`
53
+ );
54
+ }
55
+ return { key, sort };
56
+ })
57
+ );
58
+
59
+ // Wrap the comparison function so it applies to sort keys.
60
+ const defaultCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
61
+ const originalCompare = compare ?? defaultCompare;
62
+ // Sort by the sort key.
63
+ tuples.sort((a, b) => originalCompare(a.sort, b.sort));
64
+ // Map back to the original keys.
65
+ resultKeys = tuples.map((pair) => pair.key);
66
+ } else {
67
+ // Use original keys as sort keys.
68
+ // If compare is undefined, this uses default sort order.
69
+ resultKeys = treeKeys.slice().sort(compare);
70
+ }
71
+ yield* resultKeys;
72
+ },
73
+
74
+ source,
75
+
76
+ trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
77
+ });
49
78
 
50
- // Wrap the comparison function so it applies to sort keys.
51
- const defaultCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
52
- const originalCompare = compare ?? defaultCompare;
53
- // Sort by the sort key.
54
- tuples.sort((a, b) => originalCompare(a.sort, b.sort));
55
- // Map back to the original keys.
56
- const sorted = tuples.map((pair) => pair.key);
57
- return sorted;
58
- } else {
59
- // Use original keys as sort keys.
60
- // If compare is undefined, this uses default sort order.
61
- return keys.slice().sort(compare);
62
- }
63
- };
64
79
  return transformed;
65
80
  }
@@ -0,0 +1,21 @@
1
+ import SyncMap from "../drivers/SyncMap.js";
2
+ import getMapArgument from "../utilities/getMapArgument.js";
3
+ import mapReduce from "./mapReduce.js";
4
+
5
+ /**
6
+ * Resolve the async tree to a synchronous tree.
7
+ *
8
+ * @typedef {import("../../index.ts").Maplike} Maplike
9
+ *
10
+ * @param {Maplike} source
11
+ */
12
+ export default async function sync(source) {
13
+ const tree = await getMapArgument(source, "sync");
14
+ return mapReduce(tree, null, (values, keys) => {
15
+ const entries = [];
16
+ for (let i = 0; i < keys.length; i++) {
17
+ entries.push([keys[i], values[i]]);
18
+ }
19
+ return new SyncMap(entries);
20
+ });
21
+ }
@@ -1,22 +1,34 @@
1
- import getTreeArgument from "../utilities/getTreeArgument.js";
1
+ import AsyncMap from "../drivers/AsyncMap.js";
2
+ import getMapArgument from "../utilities/getMapArgument.js";
2
3
 
3
4
  /**
4
- * Returns a new tree with the number of keys limited to the indicated count.
5
+ * Returns a new map with the number of keys limited to the indicated count.
5
6
  *
6
- * @param {import("../../index.ts").Treelike} treelike
7
+ * @param {import("../../index.ts").Maplike} maplike
7
8
  * @param {number} count
8
9
  */
9
- export default async function take(treelike, count) {
10
- const tree = await getTreeArgument(treelike, "take");
10
+ export default async function take(maplike, count) {
11
+ const source = await getMapArgument(maplike, "take");
12
+ return Object.assign(new AsyncMap(), {
13
+ description: `take ${count}`,
11
14
 
12
- return {
13
- async keys() {
14
- const keys = Array.from(await tree.keys());
15
- return keys.slice(0, count);
15
+ async *keys() {
16
+ let i = 0;
17
+ for await (const key of source.keys()) {
18
+ yield key;
19
+ i += 1;
20
+ if (i >= count) {
21
+ break;
22
+ }
23
+ }
16
24
  },
17
25
 
18
26
  async get(key) {
19
- return tree.get(key);
27
+ return source.get(key);
20
28
  },
21
- };
29
+
30
+ source: source,
31
+
32
+ trailingSlashKeys: /** @type {any} */ (source).trailingSlashKeys,
33
+ });
22
34
  }